- 简介
希尔排序是插入排序的一种更高效的改进算法。对于插入排序来说,最好的情况是待排序数组本身已大部分有序或完全有序。此时进行排序接近线性排序,时间复杂度为O(n)。希尔排序的目的,就是将数组最后转换为这样的形式 ,使得插入排序的效率更高。先宏观调整,再微观调整。
关于希尔排序中最重要的变量是步长,它是逐渐减小的,每一趟排序的步长都必定比上一趟小,最终成为1.根据步长将原数组分为小段,对每一段进行直接插入排序,随后减小步长,对每一段进行直接插入排序...重复以上过程,当步长成为1的时候此时数组已经大部分有序了,尽可能的使最后这一趟插入排序的时间复杂度接近O(n).
- 关于希尔排序中步长的选取
步长的选取并没有绝对的规定和规律,只需最后步长可以变为1即可。WikiPedia上记载的有12种。
一般来说步长的选取是有以下2种情况。
①:
希尔排序的发明者最初提议是n/2即取数组长度的一半,直到变为1为止。虽然这样选取确实最后时间复杂度、数据的交换次数和比较次数都比直接插入排序好,但是仍然有改进的余地。
②:
已知的最好步长串行是由Sedgewick提出的 (1, 5, 19, 41, 109,...),该串行的项来自 9 * 4^i - 9 * 2^i + 1 和 4^i - 3 * 2^i + 1 这两个算式[1].这项研究也表明“比较在希尔排序中是最主要的操作,而不是交换。”
我们平常使用的时候没有特殊需要,只需将步长设置为n/2即可。
- 具体代码实现(步长n/2,升序,使用数组)
void ShellSort(int arr[],int len)
{
int gap = len/2;
while(gap >= 1)
{
for(int i = gap;i < len;i++)
{
for(int j = i;j >= gap && (arr[j - gap] > arr[j]); j-=gap)
{
int get = arr[j];
arr[j] = arr[j-gap];
arr[j-gap] = get;
}
}
gap/=2;
}
}
这是最简单的希尔排序实现。先指定gap的初值,再在while循环中不断减小直至为1.内部的设计非常巧妙,外层for循环定义i从gap处开始,每次循环完成后+1,在一次内层循环中则是将gap处的元素和它前面的元素进行比较,满足条件时进行交换,完成了“将数组分为多个子数组后,先将子数组排序”的过程。
随着gap的不断减小,子数组也越来越小。不断重复进行排序之后,gap变为1.此时的数组应当已经是大部分有序(完全有序也有可能)。当gap为1的时候,外层循环完美模拟了直接插入排序的过程:将第一个元素空出来,作为有序数列的首元素;每次的i代表待插入元素的下标。进入内部循环,如果满足条件的话定义get先将待插入的数保存起来防止丢失,然后直接进行交换,直至将数据交换到正确的位置。