希尔排序是带间隔的分组插入排序,按gap序列分组并逐组插入排序,gap从大到小递减至1;关键点是内层循环中j需满足j>=gap以避免越界,推荐使用Knuth序列(1,4,13,40…)提升稳定性。

希尔排序的核心是分组插入排序
希尔排序不是独立的新排序逻辑,而是把插入排序推广到“带间隔的分组”上。关键在 gap 序列:每次按 gap 把数组切成 gap 组,每组内部做插入排序;然后缩小 gap,直到 gap == 1 时变成普通插入排序。
手写时最容易错的是内层循环的起始位置和步长跳转——不能直接套用普通插入排序的 j = i - 1,而要写成 j = i - gap,且比较和移动都以 gap 为单位。
推荐用 Knuth 序列:gap = gap * 3 + 1 然后倒推
常见错误是用 gap /= 2(希尔原始序列),但实际中 Knuth 序列(1, 4, 13, 40, ...)更稳定,平均性能更好,且避免某些特殊数组退化。
实操步骤:
立即学习“C++免费学习笔记(深入)”;
- 先正向生成最大
gap:从 1 开始,while (gap ,然后 <code>gap /= 3进入第一轮 - 外层
for (int gap = max_gap; gap > 0; gap /= 3) - 内层对每个起点
i从gap开始遍历:for (int i = gap; i - 插入过程:用
temp = arr[i],j = i,循环while (j >= gap && arr[j - gap] > temp),然后arr[j] = arr[j - gap]; j -= gap;
边界检查必须包含 j >= gap,不是 j > 0
这是手写时最高频的 bug。因为元素要和 j - gap 位置比较,若只写 j > 0,当 gap > 1 时,j - gap 可能为负,越界访问。
正确写法始终是:while (j >= gap && arr[j - gap] > temp)。例如 gap = 3,i = 3,则 j 初始为 3,第一次比较 arr[0] 和 temp,合法;若 j 减到 2,j - gap = -1,此时 j >= gap 已为 false,循环终止。
C++ 实现注意引用传参和模板泛型
如果写成函数,别忘了用引用避免拷贝:void shellSort(std::vector<int>& arr)</int>。想支持多种类型,可加模板:template<typename t></typename>,但需确保 T 支持 比较。
一个极简可运行片段:
void shellSort(std::vector<int>& arr) {
int n = arr.size();
if (n <= 1) return;
int gap = 1;
while (gap < n) gap = gap * 3 + 1;
while (gap > 0) {
for (int i = gap; i < n; ++i) {
int temp = arr[i];
int j = i;
while (j >= gap && arr[j - gap] > temp) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
gap /= 3;
}
}
注意 gap /= 3 在 while 循环末尾,不是开头——否则第一轮就跳过最大 gap。
真正难的不是写出来,而是理解为什么 gap 必须从大到小、为什么不能并行处理各组、以及如何验证中间状态是否符合分组有序——这些往往被忽略,但调试时最耗时间。


















