JS 实现希尔排序

1,639 阅读2分钟

概念

希尔排序又称缩小增量排序,是插入排序的一种更高效的改进版,属于非稳定排序算法。

适用说明

希尔排序时间复杂度是 O(n^(1.3-2)),空间复杂度为常数阶 O(1)。希尔排序没有时间复杂度为 O(n(logn)) 的快速排序算法快 ,因此对中等大小规模表现良好,但对规模非常大的数据排序不是最优选择,总之比一般 O(n^2 ) 复杂度的算法快得多。

算法思想

希尔排序的基本思想是:先将整个待排序的序列分割成为若干子序列,而后将它们分别进行直接插入排序。

具体步骤:

  1. 选择一个增量序列 t1,t2,…,ti,tj,tk,其中 ti > tj,tk = 1。注意,增量是不断缩小的。
  2. 按增量序列的个数 n,对序列进行 n 趟排序。
  3. 每趟排序,根据对应的增量,将待排序的序列分割成若干长度为 m 的子序列,并分别对各子序列进行直接插入排序。当增量为 1 时,对整个序列进行直接插入排序。

图解过程

希尔排序目的是为了加快速度改进了插入排序,交换不相邻的元素对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。

这里我们选择增量 gap = length / 2,缩小增量以 gap = gap / 2 的方式,用序列 { n/2,(n/2)/2,... ,1 } 来表示。

假设待排序的序列为 [7, 6, 9, 3, 1, 5, 2, 4],示意图如下(按从小到大排序):

  1. 第一趟,初始增量 gap = length / 2 = 4,共有 4 个子序列。注意,length 表示序列的长度。

ShellSort-01.png

  1. 第二趟,增量为 gap = (length / 2) / 2 = 2,共有 2 个子序列。

ShellSort-02.png

  1. 第三趟,增量缩小为 1,整个序列进行直接插入排序,得出最终结果。

ShellSort-03.png

代码实现

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);

参考文章