对于大规模乱序数组插入排序很慢,因为它只会交换相邻的元素,因此元素只能一点一点地从数组的一端移动到另一端。例如,如果主键最小的元素正好在数组的尽头,要将它挪到正确的位置就需要N-1次移动。希尔排序为了加快速度简单地改进了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。
希尔排序的思想是使数组中任意间隔为h的元素都是有序的。这样的数组被称为h有序数组。换句话说,一个h有序数组就是h个互相独立的有序数组编织在一起组成的一个数组。
这么看根本看不懂啊。
换个说人话的模式:
假设你书架上有一堆书,高度各不相同,乱七八糟地放着。你的目标是把它们按从矮到高的顺序排好。
1. 笨方法:插入排序
一种最直接的方法是使用插入排序:
你从第二本书开始,把它拿出来,和它前面的所有书比较,找到它应该插入的位置,然后插进去。然后再处理第三本,以此类推。
问题:如果一本很矮的书恰好在最右边,你需要把它和它左边的每一本书都交换位置,一步一步地“挪”到最左边,效率非常低。就像在堵车时,你排在车队最后,只能一辆一辆地往前挪。
2. 聪明的方法:希尔排序
希尔排序想出了一个聪明的点子:“我们为什么不先大踏步地整理一下,让整体看起来‘差不多’有序,然后再做精细的微调呢?”
它的核心思想是:分组插入排序。
我们还用整理书来比喻:
第一步:先“大步长”地分组整理
-
你决定,先每隔 4本书 分成一组。(这个“3”就是“步长”或“间隔”)
-
那么,第1、5、9、13...本书成为一组。
-
第2、6、10、14...本书成为另一组。
-
第3、7、11、15...本书成为第三组。
-
现在,你只在每个小组内部,用插入排序的方法把它们排好序。
效果如何?
经过这次“大步长”整理后,虽然整体顺序还不完美,但你会发现一个神奇的现象:那些特别矮的书已经从右边大步地“跳”到了比较靠左的位置,特别高的书也“跳”到了右边。整体上,这堆书已经‘基本有序’了。
第二步:减小步长,再整理
-
你觉得“4”这个步长还是太大了,现在把步长缩小,比如缩小到 2。
-
你再重新分组:每隔2本书一组(第1、3、5、7...本一组;第2、4、6、8...本一组)。
-
然后,再次在每个小组内部进行插入排序。
因为现在整体已经“基本有序”了,所以这次小组内的插入排序会进行得非常快。
第三步:最后微调
-
最后,你把步长缩小到 1。这意味着什么?意味着你把所有书看成一个大组。
-
此时,因为这堆书已经非常接近完全有序的状态了,所以你只需要进行最后一次全体的插入排序,稍微调整一下个别书籍的位置,整个排序就迅速完成了!
总结一下希尔排序的精髓:
-
化整为零:它不像插入排序那样一开始就处理整个序列,而是先将一个大序列分割成若干个小序列。
-
由粗到精:它先使用一个较大的“步长”进行排序,让元素能进行大距离的移动,快速消除那些极度无序的情况。
-
逐步细化:然后逐步减小步长,序列也随之变得越来越有序。
-
最终整合:当步长减小到1时,序列已经基本有序,此时进行最后一次标准的插入排序,效率会非常高。
为什么它比简单的插入排序好?
-
减少了不必要的移动:在初期大步长的时候,一个小元素可以“跳”过很多位置,快速接近它的最终位置,不用像插入排序那样一步一步地挪。
-
利用了基本有序的优势:插入排序对“基本有序”的序列排序效率极高。希尔排序的前期工作就是为了创造一个“基本有序”的局面。
所以,你可以把希尔排序看作是插入排序的一个威力加强版,它通过“先宏观,再微观”的策略,极大地提升了排序效率。
实际上希尔排序用的是1,4,13,40,121,264,1093……这个h /= 3来操作的,最后给出代码,代码是我抄的,我写不出来sign
import java.util.Random;
public class ShellSort {
public static void sort(int [] ints){
if(ints == null || ints.length <= 1) return;
int n = ints.length;
int h = 1;
while (h < n/3) h = 3 * h + 1;//1,4,13,40,121……
while(h >= 1){
for (int i = h;i < n;i++){
for(int j = i;j >= h && ints[j] < ints[j-h];j -= h){
int temp = ints[j];
ints[j] = ints[j-h];
ints[j-h] = temp;
}
}
h /= 3;
}
}
public static void main(String [] args){
int [] a = new int[20];
Random r = new Random();
for ( int i = 0;i < a.length;i++) a[i] = r.nextInt(200);
System.out.print("排序前:");
for(int outprint : a){
System.out.print(outprint + " ");
}
sort(a);
System.out.print("\n\r排序后:");
for(int outprint : a){
System.out.print(outprint + " ");
}
}
}
3564

被折叠的 条评论
为什么被折叠?



