排序算法
id |
排序算法 |
最好情况 |
平均情况 |
最坏情况 |
空间 |
稳定性 |
方法 |
原地排序 |
备注 |
1 |
选择排序 |
$n^2$ |
$n^2$ |
$n^2$ |
$1$ |
不稳定 |
选择 |
是 |
每次选最小的与第i个元素交换,是数据移动次数最少的算法,注意,但不是比较次数最少的算法 |
2 |
冒泡排序 |
$n$ |
$n^2$ |
$n^2$ |
$1$ |
稳定 |
交换 |
是 |
从头开始每次确定一个最大的,冒泡到尾,然后数组量减一,继续冒泡 |
3 |
直接插入排序 |
$n$ |
$n^2$ |
$n^2$ |
$1$ |
稳定 |
插入 |
是 |
像整理扑克牌 |
4 |
折半插入排序 |
$nlogn$ |
$n^2$ |
$n^2$ |
$1$ |
稳定 |
插入 |
是 |
平均情况下比直接插入排序比较次数少,但元素移动次数不变;但在最好情况下,直接插入只比较一次,而折半比较logn次 |
5 |
希尔排序 |
$nlogn$ |
$nlogn$ or $n^{5/4}$ |
depands on gap, best known is $nlogn$ |
1 |
不稳定 |
插入 |
是 |
时间复杂度需要通过gap确定 |
6 |
快速排序 |
$nlogn$ |
$nlog$ |
$n^2$ |
$logn$ |
不稳定 |
切分 |
是 |
递归空间$logn$;头只移动一次,交换前后两个指针;头常移动,和两个指针来回交换;三向切分,用于重复多的数组 |
7 |
堆排序 |
$nlogn$ |
$nlogn$ |
$nlogn$ |
$1$ |
不稳定 |
选择 |
是 |
从小到大排序,用大根堆 |
8 |
归并排序 |
$nlogn$ |
$nlogn$ |
$nlogn$ |
$n$ |
稳定 |
归并 |
否 |
递归,自顶向下;慢慢合并,自底向上 |
9 |
基数排序 |
$d(n+k)$ |
$d(n+k)$ |
$d(n+k)$ |
$n+k$ |
稳定 |
- |
否 |
d表示位数,比如2,k表示进制,比如10。基数排序属于非比较算法,适用于整数,不适用于字符串 |
算法的选择
id |
排序算法 |
最好情况 |
平均情况 |
最坏情况 |
空间复杂度 |
稳定性 |
方法 |
n较小,无稳定型要求 |
1 |
选择排序 |
$n^2$ |
$n^2$ |
$n^2$ |
$1$ |
不稳定 |
选择 |
n较小,有稳定性要求;整体有序 |
2 |
冒泡排序 |
$\mathbf n$ |
$n^2$ |
$n^2$ |
$1$ |
稳定 |
交换 |
3 |
直接插入排序 |
$\mathbf n$ |
$n^2$ |
$n^2$ |
$1$ |
稳定 |
插入 |
n较大,无稳定性要求,比较随机 |
6 |
快速排序 |
$nlogn$ |
$nlog$ |
$n^2$ |
$logn$ |
不稳定 |
切分 |
n较大,无稳定性要求,比较有序 |
7 |
堆排序 |
$nlogn$ |
$nlogn$ |
$nlogn$ |
$1$ |
不稳定 |
选择 |
n较大,有稳定性要求,比较有序,空间允许的情况下 |
8 |
归并排序 |
$nlogn$ |
$nlogn$ |
$nlogn$ |
$n$ |
稳定 |
归并 |
原地排序:在排序输入数组时,只有常数个元素被存放到数组以外的空间中去。
1.选择排序
- 找到数组中最小的那个元素
- 将它和数组中的第一个元素交换
- 在剩下的元素中找到最小的元素,与第二个元素交换
- 如此往复。
1 2 3 4 5 6 7 8 9 10 11
| public static void selectSort(Comparable<?>[] a){ int N = a.length; for(int i=0; i<N; ++i){ int min=i; for(int j=i+1; j<N; ++j){ if(less(a[j],a[min])) min = j; } exch(a, i, min); } }
|
运行时间和输入无关,即不会因为初始有序而提高效率
如果已经是有序,那么是时间复杂度还是平方级的。
数据移动是最少的
2.冒泡排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public static void bubbleSort(Comparable<?>[] a){ int N = a.length; int flag = 1; for(int i=1; i<N; ++i){ flag = 1; for(int j=0; j<N-i; ++j){ if(more(a[j],a[j+1])){ exch(a, j, j+1); flag = 0; } } if(flag == 1) break ; } }
|
3.直接插入排序
下一个元素倒序与前面的元素一个个比较,插入到前面的合适位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public static void insertSort(Comparable<?>[] a){ int N = a.length; for(int i=0; i<N; ++i){ for(int j=i; j>0 && less(a[j],a[j-1]); --j){ exch(a, j, j-1); } } } public static void insertSort2(Comparable<?>[] a){ int N = a.length; for(int i=0; i<N; ++i){ Comparable<?> t = a[i]; int j = i; for(; j>0 && less(t,a[j-1]); --j){ a[j] = a[j-1]; } a[j]=t; } }
|
取决于初始的状态,如果已经是有序,那么是时间复杂度线性的。
插入排序对于部分有序的数组十分高效。
4.折半插入
下一个元素折半与前面的元素比较,插入到前面的合适位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public static void binsertSort(Comparable<?>[] a){ int N=a.length; for(int i=1; i<N; ++i){ Comparable<?> t = a[i]; int low = 0; int high = i-1; int m = 0; while(low<=high){ m = (low+high)/2; if(less(a[i],a[m])){ high = m-1; }else{ low = m+1; } } for(int j=i; j>low; --j){ a[j] = a[j-1]; } a[low] = t; } }
|
5.希尔排序
缩小增量法排序
- 设置间隔值h,分成h个组,在组内排序,得到h个有序子数组
- 缩小h值,分成h个组,在组内排序,得到h个有序子数组
- 直到h<1,停止,得到有序数组
间隔的取法
General term(k>=1) |
Concrete gaps |
Worst-case time complexity |
$\lfloor \frac{N}{2^k} \rfloor$ |
$\lfloor \frac{N}{2} \rfloor,\lfloor \frac{N}{4} \rfloor,…,1$ |
$O(N^2)$ |
$2^k-1$ |
1,3,7,15,31,63,… |
$O(N^{3/2})$ |
$\frac{3^k-1}{2}$,且$\leqslant\lceil \frac{N}{3}\rceil$ |
1,,4,13,40,121,… |
$O(N^{3/2})$ |
… |
… |
… |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public static void shellSort(Comparable<?>[] a){ int N = a.length; int h = 0; while(h<N/3){ h = 3*h+1; } while(h>=1){ for(int i=h; i<N; ++i){ for(int j=i; j>=h && less(a[j], a[j-h]); j=j-h){ exch(a, j, j-h); } } h=h/3; } }
|
6.快速排序
v为头元素
从左边向右,发现第一个大于等于v的元素,
从右边向左,发现第一个小于等于v的元素
交换这两个元素,然后重复
直到左指针向右,指到右指针,右指针再向左一格
现在右指针<=左指针,右指针比v小,右指针与v交换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| private static int partition1(Comparable<?>[] a, int low, int high){ int i = low, j = high+1; Comparable<?> v = a[low]; while(true){ while(less(a[++i], v)){ if(i==high) break; } while(less(v, a[--j])){ if(j==low) break; } if(i>=j) break; exch(a, i, j); } exch(a, low, j); return j; } public static void quickSort1(Comparable<?>[] a, int low, int high){ if(high <= low) return; int j = partition1(a, low, high); quickSort1(a, low, j-1); quickSort1(a, j+1, high); } private static int partition2(Comparable<?>[] a, int low, int high){ int i=low, j=high; int res = 0; while(i<=j){ while(less(a[i], a[j])) j--; exch(a, i, j); i++; if(i>=j){ res = j; break; } while(less(a[i], a[j])) i++; exch(a, i, j); j--; if(i>=j){ res = i; break; } } return res; } public static void quickSort2(Comparable<?>[] a, int low, int high){ if(high<=low) return; int j = partition2(a, low, high); quickSort2(a, low, j-1); quickSort2(a, j+1, high); } public static void quickSort3(Comparable<?>[] a, int low, int high){ if(high<=low) return; int lt=low; int i=low+1; int gt=high; Comparable<?> v = a[low]; while(i<=gt){ if(less(a[i],v)) exch(a, lt++, i++); else if(more(a[i],v)) exch(a, i, gt--); else i++; } quickSort3(a, low, lt-1); quickSort3(a, gt+1, high); }
|
7.堆排序
从小到大排序
- 构建大根堆。从最后一个非叶节点(k=N/2)开始sink(1,k),直到根节点(k=1)
- 交换大根与最后一个节点,然后sink(1,–N),直到 N=1
本来堆排序,从1开始比较方便索引的比较,但为了和别的排序一致,所以也改从0开始。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| private static void sink(Comparable<?>[] a, int k, int N){ while(2*k+1<=N){ int j = 2*k+1; if(j<N && less(a[j],a[j+1])) j++; if(!less(a[k],a[j])) break; exch(a,k,j); k=j; } } public static void heapSort(Comparable<?>[] a){ int N = a.length-1; for(int k=(N-1)/2; k>=0; --k){ sink(a,k,N); } while(N>0){ exch(a,0,N--); sink(a,0,N); } }
|
8.归并排序 插入
两个子数组的归并
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| static Comparable<?>[] aux; public static void merge(Comparable<?>[] a, int low, int mid, int high){ int i = low, j= mid+1; for(int k = low; k<=high; ++k){ aux[k] = a[k]; } for(int k=low; k<=high; ++k){ if(i>mid) a[k] = aux[j++]; else if(j>high) a[k] = aux[i++]; else if(less(aux[i],aux[j])) a[k] = aux[i++]; else a[k] = aux[j++]; } }
|
8.1自顶向下归并排序
递归归并
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public static void topDownMergeSort(Comparable<?>[] a){ aux = new Comparable[a.length]; tdsort(a, 0, a.length-1); } public static void tdsort(Comparable<?>[] a, int low, int high){ if(high<=low) return; int mid = (low+high)/2; tdsort(a,low, mid); tdsort(a,mid+1, high); merge(a,low, mid, high); }
|
8.2自底向上归并排序
1 2 3 4 5 6 7 8 9 10 11 12 13
| public static void downTopMergeSort(Comparable<?>[] a){ int N = a.length; aux = new Comparable[a.length]; for(int sz = 1; sz < N; sz=sz+sz){ for(int low = 0; low<N-sz; low=low+sz+sz){ merge(a, low, low+sz-1, Math.min(low+sz+sz-1, N-1)); } } }
|
9. 基数排序
前面的算法都是利用比较和交换,而基数排序则是利用分配和收集。
这种排序一般仅适用于整数类型的排序,对于字符串不适合。
LDS(least significant digit)低位优先
比如,15,13(a),7,13(b)。按个位排,13(a),13(b),15,7,接下来按十位排7,13(a),13(b),15 正确
MDS(most significant digit)高位优先
不能完全按照LDS的方式进行,比如,15,13(a),7,13(b)。按十位排,7,15,13(a),13(b),接下来按个位排13(a),13(b),15,7 错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| private static int getDigit(int x, int d) { int tmp = 1; while(d>0){ tmp = tmp*10; d--; } return (x%tmp)/(tmp/10); } public static void radixSortLSD(Comparable<?>[] a, int d){ int N = a.length; final int k = 10; int[] count = new int[k]; Comparable<?>[] bucket = new Comparable<?>[N]; int i = 0; int j = 0; for(int id=1; id<=d; ++id){ for(i=0; i<k; ++i){ count[i] = 0; } for(i=0; i<N; ++i){ j = getDigit((int)a[i], id); count[j]++; } count[k-1] = N - count[k-1]; for(i = k-2; i>=0; --i){ count[i] = count[i+1] - count[i]; } for(i = 0; i<N; ++i){ j = getDigit((int)a[i], id); bucket[count[j]++] = a[i]; } for(i = 0; i<N; ++i){ a[i] = bucket[i]; } } }
|
Reference
https://en.wikipedia.org/wiki/Sorting_algorithm
https://www.douban.com/note/250011027/?type=like
http://blog.csdn.net/CJF_iceKing/article/details/7953637
http://www.cnblogs.com/jingmoxukong/p/4311237.html