希尔排序
希尔排序是插入排序的一种高效的改进版,并且效率比插入排序要更快,因为它突破了时间复杂度为O(n^2的限制)。由于希尔排序是基于插入排序的,所以有必要回顾一下插入排序:
插入排序的问题:
1,假设一个很小的数据项在很靠近右端的位置上,这里本来应该是较大的数据项的位置。
2,把这个小数据项移动到左边的正确位置,那么所有的中间数据项都必须向右移动一位。
3,因此我们通常认为插入排序的时间复杂度为O(n^2)。
4,如果有某种方式,不需要一个个移动所有中间的数据项,就能把较小的数据项移动到左边,那么这个算法的执行效率就会有很大的改进。
希尔排序的思路:
以下图为例。
1,我们先让间隔为5的数字进行一个比较并进行排序,那么排序后的新序列,一定可以让每个数字距离自己的正确位置更近一步。
2,紧接着我们再让间隔为3的数字进行一个比较并进行排序,那么排序后的新序列,一定可以让每个数字距离自己的正确位置又更近一步。
3,最后,我们再让间隔为1的数字进行,也就是正确的插入排序了。
但是这里有一个问题就是如何去选择我们的一个间隔呢也就是增量呢?
希尔在自己原稿中的做法是:他建议的初始间隔为N/2,简单的把每趟排序分成两半,也就是说,对于N=100的数组,增量间隔序列为:50,25,12,6,3,1。而这个做法的好处在于不需要在开始排序前为找合适的增量再去进行其它的计算。
当然也可以了解一下其它的两种计算增量的方法。
1,Hibbard增量序列:增量的算法为2^k - 1,也就是1,3,5,7…等等,而这种增量的最坏复杂度为O(N3/2),猜想的平均复杂度为O(N5/4),但是目前尚未被证明。
2,Sedgewick增量序列:这种增量的最坏复杂度为O(N/4/3),平均复杂度为O(N/7/6),但是目前也未被证明。
function shellSort(arr) {
//根据长度计算增量
let gap = Math.floor(arr.length / 2);
//根据gap的不断减小从而不断进行循环
while (gap >= 1) {
//以gap为间隔进行分组,然后就是插入排序的思路了
for (let i = gap; i < arr.length; i++) {
let temp = arr[i];
let j = i;
while (arr[j - gap] > temp && j > gap - 1) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
//重新计算新的间隔
gap = Math.floor(gap / 2);
}
return arr;
}
console.log(shellSort([1, 3, 2, 6, 5, 4]));
希尔排序的效率:最坏的情况下时间复杂度为O(n^2),但是通常情况下都好于它,甚至在合适的增量以及数量的情况下,它比快速排序还好。