排序算法-插入排序
插入排序法
内容来自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)。