想象你是体育老师,要按身高给全班50个学生排队(升序)。如果直接一个个调整位置(插入排序),效率很低。希尔排序就像一套聪明的“分组训练法”:
-
第一轮:大间隔分组(gap=10)
- 把学生每10人分一组(共5组)
- 每组内部按身高调整顺序 → 矮在前,高在后
- 结果:组内有序,但整体仍乱(如第1组最矮的可能比第2组最高的还高)
-
第二轮:缩小间隔(gap=5)
- 重新每5人分一组(共10组)
- 再次每组内部排序
- 结果:整体更有序(相邻学生身高接近)
-
第三轮:最小间隔(gap=1)
- 全班视为1组,整体微调 → 只需少量移动即完全有序
核心思想:
通过动态缩小的间隔(gap) 将数据分组,每组用插入排序预处理,使数据逐步趋于有序。当gap=1时,数据已“基本有序”,最后一次插入排序效率极高
⚙️ Java代码实现(逐行解析)
java
Copy
public class ShellSort {
// 基础版(使用Shell原始序列:gap = n/2, n/4, ... 1)
public static void shellSort(int[] arr) {
int n = arr.length;
// 初始间隔取数组长度的一半
for (int gap = n / 2; gap > 0; gap /= 2) {
// 从gap位置开始,对每个子序列做插入排序
for (int i = gap; i < n; i++) {
int temp = arr[i]; // 当前要插入的元素
int j = i;
// 在子序列内向前比较(间隔为gap)
while (j >= gap && arr[j - gap] > temp) {
arr[j] = arr[j - gap]; // 大元素后移
j -= gap; // 跳到同组前一个位置
}
arr[j] = temp; // 插入到正确位置
}
}
}
// 优化版(使用Sedgewick序列,效率更高)
public static void shellSortOptimized(int[] arr) {
int[] gaps = {1, 5, 19, 41, 109}; // Sedgewick序列
int n = arr.length;
// 倒序遍历间隔序列(从大到小)
for (int k = gaps.length - 1; k >= 0; k--) {
int gap = gaps[k];
if (gap > n / 2) continue; // 跳过过大的间隔
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;
}
}
}
public static void main(String[] args) {
int[] heights = {170, 155, 175, 160, 165};
shellSort(heights);
System.out.println("排序后: " + Arrays.toString(heights));
// 输出: [155, 160, 165, 170, 175]
}
}
🔍 关键步骤图解(以数组 [8, 2, 11, 7, 14, 5] 为例)5
-
初始 gap=3:
- 分组:
[8, 7],[2, 14],[11, 5] - 组内排序:
[7, 8],[2, 14],[5, 11]→ 数组变为[7, 2, 5, 8, 14, 11]
- 分组:
-
第二轮 gap=1:
-
整体插入排序:
- 取
2→ 插入到7前 →[2, 7, 5, 8, 14, 11] - 取
5→ 插入到7前 →[2, 5, 7, 8, 14, 11] - 取
11→ 插入到14前 →[2, 5, 7, 8, 11, 14]
- 取
-
💡 为何高效?
前期大gap:元素大幅跳跃移动,快速消除大范围无序
后期小gap:数据已基本有序,插入排序只需少量调整
📊 性能与特点分析
| 特性 | 说明 |
|---|---|
| ⏱️ 时间复杂度 | 取决于间隔序列: - Shell序列:平均 O(n^1.3) - Sedgewick序列:O(n^(4/3)) 79 |
| 📦 空间复杂度 | O(1)(原地排序) |
| ⚠️ 稳定性 | 不稳定(相同元素可能被分到不同组交换位置)610 |
| 💡 适用场景 | 中等规模数据、内存受限环境、嵌入式系统 |
🌟 对比其他排序:
vs 插入排序:希尔排序通过分组预处理,将移动次数从 O(n²) 降至 O(n log n)
vs 快速排序:虽不如快排快,但无最坏情况O(n²),且代码更简单
❓ 小白常见疑问
-
为什么分组能提高效率?
大间隔使小元素快速前移,大元素快速后移,大幅减少后期调整次数 -
间隔序列怎么选?
-
n/2, n/4, ... 1:简单但非最优 -
Sedgewick序列 (1,5,19,41...):数学证明效率更高
-
-
代码中
j >= gap的作用?
防止数组越界!确保比较时j - gap不超出数组边界
💎 总结
希尔排序 = “动态间隔 + 分组插入”:
- 选间隔:从大到小(如
n/2 → n/4 → ... → 1) - 分组排序:每组用插入排序预处理
- 逐步收束:间隔减至1时,整体微调即有序
🚀 历史意义:首个突破O(n²)的排序算法,至今仍是嵌入式系统和中等数据量的实用选择!