首先呢,希尔排序又称“缩小增量排序”(Diminishing Increment Sort),是插入排序的优化。
希尔排序都对插入排序做了哪些优化呢?
如下:
- 希尔排序在排序前:将一个序列分成了好几个序列
- 在第一趟排序时:将这几个序列做插入排序。排序后,部分较大的数字往后靠,部分较小的数字往前靠
- 在第二趟排序时:将这个序列又分了好几个序列做插入排序(但比第一次分的数要少,ps:如果第一次分5个,第二次可能就2个了)。排序后,部分较大的数字往后靠,部分较小的数字往前靠
- 在第n趟排序时:将这个序列又分了好几个序列(直到剩下一个序列),从宏观上看,此序列就基本是有序的了。这时就用简单插入排序将数列直至已序
有点像归并排序,归并排序是将待排序的数组(递归的)分成两半,对这两部分分别排序,然后将结果归并起来,得到结果,体现了分治的思想。
希尔排序是有个gap值,这个排序的分组是隔gap个位置的两个元素分一组,也就是说相隔gap位置的元素必定是有序的,归并排序并没有这个规定。
当然这两个排序算法的时间复杂度都是O(nlogn)。
直观来说:
如果一个数列有10个元素,我们第一趟的增量是5,第二趟的增量是2,第三趟的增量是1。
如果一个数列有18个元素,我们第一趟的增量是9,第二趟的增量是4,第三趟的增量是2,第四趟的增量是1
如果用一个序列来表示增量:{n/2,(n/2)/2...1},每次增量都除2
这张图解写得很清楚,搬运一下:
最后上一下代码(如果把上述原理理解的话,代码是很好理解的):
public static void shellSort(int[] arrays) {
//增量step每次都除2
for (int step = arrays.length / 2; step > 0; step /= 2) {
//从增量那组开始进行插入排序,直至完毕
for (int i = step; i < arrays.length; i++) {
int j = i;
//定义temp来保存i位置的元素,方便一会儿移动
int temp = arrays[j];
// j - step 就是代表与它同组隔壁的元素
while (j - step >= 0 && arrays[j - step] > temp) {
arrays[j] = arrays[j - step];//大元素后移
j = j - step;
}
//直到找到合适位置,插入temp
arrays[j] = temp;
}
}
}