最近在书上看到一个习题说能把插入排序的复杂度通过改变插入规则为二分法从而降低时间复杂度到nlgn,我自己下来试了试发现想法倒是没问题,但实现起来有点小难点。
一般的插入排序时间复杂度为O(n2)毋庸置疑,但是用二分法插入同样需要移动大量数组元素,查找下标的时间复杂度降低到了O(nlgn),但是移动数组元素的时间复杂度依然是O(n2)……哪位大神赐教赐教~
插入排序时间复杂度分析:
public static void insertionSort(int[] arr) { for (int j = 1; j < arr.length; j++) { //c1*n int key = arr[j]; //c2*n int i; //c3*n for (i = j - 1; i >= 0 && arr[i] > key; i--) { //c4*n*(n+1)/2 arr[i + 1] = arr[i]; //c5*n*(n+1)/2 } arr[i + 1] = key; //c6*n } }
时间复杂度T(n)=(c4+c5)*n*(n+1)/2+(c1+c2+c3+c6)*n=O(n2);
改进后的插入排序:
/*插入排序的改进版,用二分法插入从而取代一次次的比较,减小了n*n-nlgn的时间复杂度*/
public static void inproveInsertionSort(int[] arr) { for (int j = 1; j < arr.length; j++) { //c1*n int key = insertToArray(arr,0,j-1,j); //c2*n*log2n int temp=arr[j]; //c3*n for(int i=j;i>key;i--){ //c4*n*(n+1)/2 arr[i]=arr[i-1]; //c5*n*(n+1)/2 } arr[key]=temp; //c6*n } } /*二分法查找key应该插入有序数组arr【b,e】的位置,返回应当插入的数组index,递归查询*/ public static int insertToArray(int[]arr,int b,int e,int key){ //nlog2n if(b>=e){ if(arr[key]<arr[b]){ return b; } else return b+1; } int mid=(b+e)/2; if(arr[key]<=arr[mid])return insertToArray(arr,b,mid,key); return insertToArray(arr,mid+1,e,key) ; }
如果用其他的数据结构【1】而不用数组省去移动数组操作的话,也就是省去c4c5部分。则时间复杂度为:
T(n)=c2*n*lgn+(c1+c3+c6)n=O(nlgn)----------------------------------------lgn=log2n
时间复杂度降低到比较排序的下Ω(nlgn),可以和合并排序媲美了~
【1】:这里说的用其他数据结构就是比如链表,但是又出现一个新问题,用链表虽然去除了大量移动数组元素带来的时间占用,同时也带来了二分法查找数据的困难。目前我还没想到办法实现。引用第二版算法导论译本,上面也只有改进的习题但是没有答案~就先到这里吧。
引用:算法导论第二版
2016-11-01