@TOC
几种初级排序算法的总结
排序就是将一组对象按照某种逻辑顺序重新排列的过程。
在本文中我们使用的操作:
- 遍历数组
- 比较两个对象(本文中所有代码示例中的less方法)
- 交换两个对象 (本文中所有代码示例中的exch方法)
less 和 exch方法的源码:
private static boolean less(Comparable v, Comparable w) {
return (v.compareTo(w) < 0);
}
private static void exch(Comparable[] a, int i, int j) {
Comparable swap = a[i];
a[i] = a[j];
a[j] = swap;
}
选择排序
选择排序的基本思路就是为每一个位置选择对的对象:
- 选择第一个位置;
- 从数组中未被选择的对象中选择最小(或最大)的对象放入当前选择的位置;
- 选择下一个位置,并重复2直到所有位置都有对象;
代码示例:
public static void sort(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.直到当前选择的对象前面没有元素或者,它和前面元素的相对位置是正确的;(一步一步往前挪)
- 选择下一个位置的元素,并重复2直到数组末尾;
示例代码:
public static void sort(Comparable[] a) {
int n = a.length;
for (int i = 1; i < n; i++) { //选择需要挪动的对象
for (int j = i; j > 0; j--) { //一步一步往前挪
if(less(a[j], a[j-1])) //如果当前元素比前一个小,则交换位置
exch(a, j, j-1);
else
break; //已经挪到了对的位置,去挪下一个
}
}
}
希尔排序
希尔排序是一种基于插入排序的快速排序算法;对于大规模的乱序数组,插入排序会很慢,因为它只交换相邻的对象,元素要一点一点的往前挪;希尔排序的基本思路是分别对间隔h的元素组成的数组进行排序,使得原数组h有序,然后逐步减小h,如下图:
- 选取一个h;
- 对间隔h的对象组成的数组进行插入排序;
- 减小h,重复2一直到h为1;
代码示例:
public static void sort(Comparable[] a) {
int n = a.length;
// 3x+1 increment sequence: 1, 4, 13, 40, 121, 364, 1093, ...
int h = 1;
while (h < n/3) h = 3*h + 1; //取一个大于等于数组长度三分之一的h
while (h >= 1) {
// h-sort the array
for (int i = h; i < n; i++) { //使用插入排序对间隔h的对象组成的数组进行排序
for (int j = i; j >= h && less(a[j], a[j-h]); j -= h) {
exch(a, j, j-h);
}
}
h /= 3; //按3的倍数减小h
}
}
性能与特性
- 稳定性:选择排序的稳定性与具体实现有关,主要是判断大小的条件对对象相等的时候的处理,在我们的实现中它是不稳定的,因为它会交互两个相等的对象;插入排序是稳定的,因为挪动到相等元素时会停止;而基于插入排序的希尔排序却是不稳定的,因为分组的过程中会间接打乱相等对象的相对位置。
- 空间:这三个算法都是直接在原数组上进行操作,并没有申请额外空间,且无递归,所以空间复杂度为O(1)。
- 时间:选择排序的时间复杂度为O($ N^2 ) ; 插 入 排 序 介 于 O ( N ) 到 O ( );插入排序介于O(N) 到 O( );插入排序介于O(N)到O( N^2$) 之间,主要依赖于数组的有序程度,数组的有序程度越高,算法的效率越好;希尔排序大概是O(NlogN) ,同样和数组的有序程度相关,不过通过分组使得随机情况下算法的性能比插入排序更好。
**一点猜想:**从选择排序到希尔排序的性能变化,可能对应于算法对已排序对象的信息的利用程度的变化;比如一个数组中a、b、c是已经排好相对位置的元素,此时决定元素d的位置时,如果我们知道d>c,那么可以推理得出d>b以及d>a,选择排序对此类信息利用的相对较少,而插入排序相对利用的多些。
参考资料
- 算法(第四版)Robert Sedgewick 和 Kevin Wayne 著
- https://algs4.cs.princeton.edu/21elementary/ (文中所有代码来源)