排序算法-插入排序

插入排序法

内容来自mkw 《算法与数据结构体系课-插入排序法》,侵权请联系删除

1.排序思想

循环不变量:对于长度为n的数组a(索引范围[0,n-1]),a[0,i)已经排好序,a[i...n)未排序,每一循环把 arr[i]放到a[0,i)中的合适的位置。

以升序为例,arr[i] 与 arr[0,i)范围内从后往前比较,每次都比较arr[i]与arr[i-1] (前面的一个元素)的大小,如果 arr[i] < arr[i-1],就调换二者的位置,然后继续向前循环比较,直到arr[i] >= arr[i-]就找到了其在个范围的位置,此次内部循环结束,外部循环又一次i+1,然后继续内部循环,其实这里就已经用到了双指针的思想了,至少在代码层面上使用了两个指针。

与选择排序的区别:选择是每次选当前元素后面未排序中的最小的一个,与当前元素比。而这个位置确定后就是最终排序的位置,而插入排序只关注当前关注的元素前面的排序是正确的,但是不保证目前排序的元素位置不一定是最终排序的位置。如下图所示。

2.代码实现

SortingHelper.java

package 插入排序法;

public class SortingHelper {
    private SortingHelper(){}

    public static <E extends Comparable<E>>  boolean isSorted(E[] arr){
        for(int i=1; i<arr.length; i++)
            if(arr[i-1].compareTo(arr[i]) >0)
                return false;
        return true;
    }

    public static <E extends Comparable<E>>void sortTest(String sortname, E[] arr) {
        Long startTime = System.nanoTime();
        if(sortname.equals("InsertionSort"))
            InsertionSort.sort(arr);
        else if(sortname.equals("InsertionSort2"))
            InsertionSort.sort2(arr);
        Long endTime = System.nanoTime();
        double time = (endTime - startTime) / 1000000000.0;
        if(!SortingHelper.isSorted(arr))
            throw new RuntimeException(sortname + "failed");
        System.out.println(String.format("%s,n = %d : %f s", sortname, arr.length, time));
    }
}

InsertionSort() 代码 以及测试运行结果比较

package 插入排序法;
import java.util.Arrays;

public class InsertionSort {
    private InsertionSort(){}

    public static <E extends Comparable<E>>void sort(E[] arr) {
        for(int i=0; i< arr.length; i++) {
            // BETA1
            //for(int j=i; j-1>=0; j--) {
            //if(a[j].compareTo(a[j-1]) <0)
            //    swap(arr, j, j-1);
            //else break;
            for (int j=i; j-1>=0 && arr[j].compareTo(arr[j-1]) < 0 ; j--) {
                swap(arr, j,j-1);
            }
        }
    }

    public static <E> void swap(E[] arr, int i, int j) {
        E temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    //排序优化 sort2
    public static <E extends Comparable<E>> void sort2(E[] arr) {
        for(int i=0; i<arr.length; i++) {
            //将arr[i]插入到合适位置
            E t= arr[i];
            int j;
            for(j=i; j-1>=0 && t.compareTo(arr[j-1]) < 0; j--) {
                arr[j] = arr[j-1];
            }
            arr[j] = t;
        }
    }

    public static void main(String[] args) {
//         Integer[] test1 = {6,1,2,5,4,3,7};
//         InsertionSort.sort(test1);
//         for(Integer t:test1)
//             System.out.println(t + " ");

         int[] dataSize = {10000, 100000};
         for(int n:dataSize){
             Integer[] arr = ArrayGenerator.generateRandomArray(n, n);
             Integer[] arr2 = Arrays.copyOf(arr, arr.length);

             SortingHelper.sortTest("InsertionSort", arr);
             SortingHelper.sortTest("InsertionSort2", arr2);
         }

    }
}

优化的思想是将 将每次两两交换(每次交换三次操作) 改为 赋值操作。某一次运行结果如下:

InsertionSort,n = 10000 : 0.121047 s
InsertionSort2,n = 10000 : 0.098448 s
InsertionSort,n = 100000 : 12.549190 s
InsertionSort2,n = 100000 : 8.184306 s

插入排序算法的特性

在被排序数组近似有序的情况,插入排序的算法复杂度是 O(n)级别的,而选择排序永远都是O(n^2),这也是二者的又一大区别

当然一般情况下时间复杂度 选择和插入 都是O(n^2)。

posted @ 2021-01-22 19:40  又一个蛇佬腔  阅读(78)  评论(0编辑  收藏  举报