希尔排序(Shellsort)又叫做缩小增量排序(diminishing increment sort)。
希尔排序使用一个序列h1,h2,h3,....hn,叫做增量序列。只要h1=1,任何增量序列都是可行的,不过,有些增量序列比另外一些增量序列更好。在使用增量hk的一趟排序之后,对于每一个i我们有A[i]<=A[i+hk];所有相隔hk的元素被排序。此时称文件hk-排序。
初始 81 94 11 96 12 35 17 95 28 58 41 75 15
在5-排序后 35 17 11 28 12 41 75 15 96 58 81 94 95(每五个一分组然后我们对每列进行排序)
在3-排序后 28 12 11 35 15 41 58 17 94 75 81 96 95(然后每三个一分组并对列进行排序)
在1-排序后 11 12 15 17 28 35 41 58 75 81 94 95 96(进行插入排序)
希尔排序每趟之后的情况
//**以下内容来自维基百科*//
希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。
假设有一个很小的数据在一个已按升序排好序的数组的末端。如果用复杂度为O(n2)的排序(冒泡排序或插入排序),可能会进行n次的比较和交换才能将该数据移至正确位置。而希尔排序会用较大的步长移动数据,所以小数据只需进行少数比较和交换即可到正确位置。
步长序列
步长的选择是希尔排序的重要部分。只要最终步长为1任何步长序列都可以工作。算法最开始以一定的步长进行排序。然后会继续以一定步长进行排序,最终算法以步长为1进行排序。当步长为1时,算法变为插入排序,这就保证了数据一定会被排序。
Donald Shell最初建议步长选择为并且对步长取半直到步长达到1。虽然这样取可以比
类的算法(插入排序)更好,但这样仍然有减少平均时间和最差时间的余地。可能希尔排序最重要的地方在于当用较小步长排序后,以前用的较大步长仍然是有序的。比如,如果一个数列以步长5进行了排序然后再以步长3进行排序,那么该数列不仅是以步长3有序,而且是以步长5有序。如果不是这样,那么算法在迭代过程中会打乱以前的顺序,那就不会以如此短的时间完成排序了。
步长序列 | 最坏情况下复杂度 |
---|---|
| |
| |
|
这两个算式[1]。这项研究也表明“比较在希尔排序中是最主要的操作,而不是交换。”用这样步长序列的希尔排序比插入排序要快,甚至在小数组中比快速排序和堆排序还快,但是在涉及大量数据时希尔排序还是比快速排序慢。
void shell_sort(int arr[], int len) { int gap, i, j; int temp; for (gap = len >> 1; gap > 0; gap >>= 1) for (i = gap; i < len; i++) { temp = arr[i]; for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap) arr[j + gap] = arr[j]; arr[j + gap] = temp; } }
(此处摘自维基百科)