希尔排序是插入排序的一种优化方案。
希尔排序主要是利用了插入排序的一些特点:
- 小段的直接插入排序,效率很高;
- 当待排数据基本上是有序的时候,直接插入排序的效率也会很高;
假设待排序元素序列有n个元素,首先取一个整数 gap(小于n)作为间隔,将全部元素分为若干个子序列,所有距离为 gap 的元素放在同一个子序列中,在每一个子序列中进行插入排序。然后缩小间隔 gap,重复上述子序列划分、排序以及缩小 gap 的工作。直到最后取 gap=1,将所有元素放在同一个子序列中进行插入排序为止。
希尔排序非常好的利用了插入排序的特点。在刚开始时,gap 的取值较大,每个子序列中的元素较少,排序速度较快;到排序后期 gap 的取值逐渐变小,子序列中元素个数逐渐增多,但由于前面工作的基础,大多数元素已经基本有序,所以排序速度仍然很快。
希尔排序需要定义一个 gap,我们一般会选择数组长度的一半作为第一次的 gap,之后每次都将 gap 缩小为当前 gap 的一半,这是希尔建议的增量,称为希尔增量。但要知道的是,其实这个增量序列不是最优的。
希尔排序和插入排序的对比:
稳定性 | 空间复杂度 | 平均时间复杂度 | 最好时间复杂度 | 最坏时间复杂度 | |
---|---|---|---|---|---|
插入排序 | 稳定 | O(1) | O(n2) | O(n2) | O(n) |
希尔排序 | 不稳定 | O(1) | O(n1.3) | O(n2) | O(n) |
public int[] bubbleSort(int[] arr){
int len = arr.length;
// gap 是增量,每次循环结束都会减半
for (int gap = len >> 1; gap > 0; gap = gap >> 1) {
// i 起始值是组内无序区第一个元素
for (int i = gap; i < len; i++) {
int temp = arr[i];
// j 起始值是组内有序区最后一个元素
int j = i - gap;
for (; j >= 0 && arr[j] > temp; j -= gap) {
arr[j + gap] = arr[j];
}
arr[j + gap] = temp;
}
}
return arr;
}
可以到 LeetCode 中验证代码正确性:912. 排序数组
希尔排序的提交结果:
希尔排序时间复杂度的下界是n*log2n。希尔排序没有快速排序算法快。
中等大小规模表现良好,对规模非常大的数据希尔排序不是最优选择。但是比O(n2 )复杂度的算法快得多。并且希尔排序非常容易实现,算法代码短而简单。 此外,希尔算法在最坏的情况下和平均情况下执行效率相差不是很多,与此同时快速排序在最坏的情况下执行的效率会非常差。
几乎任何排序工作在开始时都可以用希尔排序,若在实际使用中证明它不够快,再改成快速排序这样更高级的排序算法.