概念
希尔排序又称缩小增量排序,是插入排序的一种更高效的改进版,属于非稳定排序算法。
适用说明
希尔排序时间复杂度是 O(n^(1.3-2)),空间复杂度为常数阶 O(1)。希尔排序没有时间复杂度为 O(n(logn)) 的快速排序算法快 ,因此对中等大小规模表现良好,但对规模非常大的数据排序不是最优选择,总之比一般 O(n^2 ) 复杂度的算法快得多。
算法思想
希尔排序的基本思想是:先将整个待排序的序列分割成为若干子序列,而后将它们分别进行直接插入排序。
具体步骤:
- 选择一个增量序列 t1,t2,…,ti,tj,tk,其中 ti > tj,tk = 1。注意,增量是不断缩小的。
- 按增量序列的个数 n,对序列进行 n 趟排序。
- 每趟排序,根据对应的增量,将待排序的序列分割成若干长度为 m 的子序列,并分别对各子序列进行直接插入排序。当增量为 1 时,对整个序列进行直接插入排序。
图解过程
希尔排序目的是为了加快速度改进了插入排序,交换不相邻的元素对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。
这里我们选择增量 gap = length / 2,缩小增量以 gap = gap / 2 的方式,用序列 { n/2,(n/2)/2,... ,1 } 来表示。
假设待排序的序列为 [7, 6, 9, 3, 1, 5, 2, 4],示意图如下(按从小到大排序):
- 第一趟,初始增量 gap = length / 2 = 4,共有 4 个子序列。注意,length 表示序列的长度。
- 第二趟,增量为 gap = (length / 2) / 2 = 2,共有 2 个子序列。
- 第三趟,增量缩小为 1,整个序列进行直接插入排序,得出最终结果。
代码实现
const list = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48];
const shellSort = arr => {
let temp,
len = arr.length,
gap = 1; // 默认增量
console.time('希尔排序耗时');
// 动态初始化增量
while (gap < len / 3) {
gap = gap * 3 + 1;
}
// 缩小每一趟的增量,直至增量为 1。增量序列的个数,决定了序列要进行排序的次数。
for (; gap > 0; gap = Math.floor(gap / 3)) {
// 根据对应的增量,将待排序列分割成若干长度的子序列,分别对各子序列进行直接插入排序。
for (let i = gap; i < len; i++) {
temp = arr[i];
let j = i - gap;
// 比较大小,交换数据进行排序
for (; j >= 0 && arr[j] > temp; j -= gap) {
arr[j + gap] = arr[j];
}
arr[j + gap] = temp;
}
console.log(`增量为 ${gap}`, arr);
}
console.timeEnd('希尔排序耗时');
return arr;
};
const arr = list.slice(); // 复制一份副本
console.log('原始数组:', list);
console.log('------------希尔排序开始------------');
shellSort(arr);