希尔排序
希尔排序是插入排序
的改进版本,弥补了插入排序在某种情况下的缺点
列如:当长度为100的数组,下标0~79为有序区域,此时我们用下标为80的元素去跟它前面的有序区域的元素比较大小,恰巧我们的下标为80的元素是100个元素中最小
的,本应在下标为0的位置,如图
本列中下标为80的元素为1,那么他前面的有序区域里面的80个元素都要向后移一个位置,此状非常影响排序性能。
因此,我们要想办法尽可能让小的靠前
,让大的靠后
,这样就可以避免上图情况,这就是希尔排序要解决的问题。
希尔排序也叫缩小增量排序
,它通过先设置一个增量n,大小为数组长度的一半
,再将间隔为n
的作为一组,然后对每组内部的元素进行从小到大的插入排序
;然后再将n缩小一半
,再次进行分组插入排序
,直到增量n为1
,因为增量为1时,所有元素都是同一组了。
//----------------分割线------------------
为了方便大家理解,我用一个例子来展示一个完整的希尔排序过程,首先数据的初始状态如图所示,这里为了更好地体现希尔排序的优点,我特地把值较大的元素放到了靠左的位置,把值较小的元素放到了靠右的位置
数组长度为8,增量 n=8/2 ,间隔n为一组;数组分组情况如下(同色为一组):
每组进行小到大的插入排序,结果如下:
此时增量为4 增量(n)不为1
,继续增量(n)=n /2 ,间隔n为一组;数组分组情况如下(同色为一组):
每组进行小到大的插入排序,结果如下:
此时增量为2 增量(n)不为1
,继续增量(n)=n /2 ,间隔n为一组;数组分组情况如下(同色为一组):
略。。。。。。。。。。。。。。
每组进行小到大的插入排序,结果如下:
动图演示
了解完了希尔排序的实现过程,我们现在用代码来封装一下:
function shellSort(arr) {
// 1. 获取数组长度
let length = arr.length
// 2.获取初始的间隔长度
let interval = Math.floor(length / 2)
// 3. 不断地缩小间隔的大小,进行分组插入排序
while(interval >= 1) {
// 4. 从 arr[interval] 开始往后遍历,将遍历到的数据与其小组进行插入排序
for(let i = interval; i < length; i++) {
let temp = arr[i]
let j = i
while(arr[j - interval] > temp && j - interval >= 0) {
arr[j] = arr[j - interval]
j -= interval
}
arr[j] = temp
}
// 5. 缩小间隔
interval = Math.floor(interval / 2)
}
return arr
}
上述情况中,希尔排序最坏情况下的时间复杂度为O(n²)。其实希尔排序的时间复杂度跟增量也有关系,我们上面是通过数组长度一直取一半获取的增量,其实还有一些别的增量规则,可以使得希尔排序的效率更高,例如Hibbard增量序列、Sedgewick增量序列