1. 简介

在本文中,我们将讨论 Shellsort 的时间复杂度。Shellsort 是一种基于插入排序的改进算法,其性能与所使用的 gap sequence(间隔序列) 紧密相关。

2. Shellsort 原理

Shellsort 利用了一个数组的性质:一个排序后的数组的任意子序列也是排序后的

举个例子,如果数组 x 是升序排列的,那么无论我们如何选取下标,这些下标的元素组成的子序列也必然是升序的。

Shellsort 的核心思想是:

  • 将数组划分为多个“链”(chains),每条链由固定间隔(gap)的元素组成
  • 对每条链进行插入排序
  • 逐步减小间隔,重复排序,直到间隔为 1(即对整个数组进行一次插入排序)

这样做的好处是:大间隔排序能让元素“跳跃”到更接近正确的位置,从而减少后续小间隔排序的工作量

2.1 示例图解

当数组长度为 16,gap = 4 时,会形成 5 条链:

Chains in Shellsort

2.2 伪代码

algorithm Shellsort(x, h):
    // x: 待排序数组
    // h: gap 序列,例如 [7, 3, 1]

    for k <- 1 to m:  // 遍历每个 gap
        for i <- 1 to h[k] + 1:
            r <- floor((n - i) / (h[k] + 1))
            Apply Insertion Sort to chain [x[i + t * (h[k] + 1)] | t = 0, 1, ..., r]

    return x

2.3 关键点总结

✅ 每次排序的是“间隔”为 h[k] 的子数组
✅ 最后一次 gap 为 1,确保最终数组有序
✅ 插入排序的高效性在“部分有序”数组中表现更佳
❌ gap 序列选择对性能影响巨大


3. 时间复杂度分析

Shellsort 的时间复杂度 完全取决于所使用的 gap sequence。目前没有统一的公式可以准确计算任意 gap 序列下的时间复杂度,因此必须 逐个分析

3.1 Shell 原始序列

Shell 最初使用的 gap 序列为:

h_k = ⌈n/2⌉, ⌈n/4⌉, ..., 1

比如 n = 16,gap 序列为:8, 4, 2, 1

最坏情况分析

当 n 是 2 的幂,且奇数位和偶数位分别有序时,插入排序在最后一次 gap=1 时会进行大量交换。

最终交换次数为:

$$ \sum_{i=1}^{n/2 - 1} i = \frac{n^2}{8} \in \Theta(n^2) $$

✅ 结论:使用 Shell 原始 gap 序列,最坏时间复杂度为 O(n²)


3.2 Hibbard 序列

Hibbard 提出的 gap 序列为:

$$ h_k = 2^k - 1 $$

比如 n = 14,gap 序列为:7, 3, 1

✅ 结论:使用 Hibbard 序列,最坏时间复杂度为 O(n¹·⁵)


3.3 Pratt 序列

Pratt 提出的 gap 序列由如下形式的数构成:

$$ 2^p \cdot 3^q $$

比如:1, 2, 3, 4, 6, 8, 9, ...

✅ 结论:使用 Pratt 序列,最坏时间复杂度为 O(n (log n)²)
⚠️ 缺点:gap 数量多,实际运行速度慢


3.4 常见 gap 序列对比

Gap Sequence 时间复杂度 备注
Shell 原始 O(n²) 简单但效率差
Hibbard O(n¹·⁵) 性能提升明显
Pratt O(n (log n)²) 当前最优理论复杂度
Sedgewick O(n⁴/³) 实用性较强
Tokuda O(n log² n) 现代实现常用

4. 空间复杂度

Shellsort 是原地排序算法,只使用常数级的额外空间。

✅ 空间复杂度:O(1)


5. 总结

Shellsort 的时间复杂度高度依赖于所使用的 gap 序列。目前尚无统一的最优 gap 序列,不同序列在理论和实际性能上表现差异显著。

关键点 说明
✅ 最优理论复杂度 O(n (log n)²)(Pratt)
❌ 最差复杂度 O(n²)(Shell 原始)
✅ 实用 gap 序列 Hibbard、Sedgewick、Tokuda
⚠️ 踩坑提示 不恰当的 gap 序列可能导致性能退化为 O(n²)
✅ 空间效率 原地排序,O(1) 空间

✅ 总结:Shellsort 是一种简单但性能依赖于 gap 序列的排序算法。合理选择 gap 可以达到接近 O(n log n) 的性能,但设计最优 gap 仍是开放问题


原始标题:The Complexity of Shellsort