跳到主要内容

希尔排序

代码

C语言代码

#include <stdio.h>

void shell_sort(int arr[], int n) {
for (int gap = n / 2; gap > 0; gap /= 2) { // 初始步长为n/2,每次减半
for (int i = gap; i < n; i++) {
int temp = arr[i];
int j;
for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
arr[j] = arr[j - gap];
}
arr[j] = temp;
}
}
}

int main() {
int arr[] = {12, 34, 54, 2, 3};
int n = sizeof(arr) / sizeof(arr[0]);

shell_sort(arr, n);

printf("Sorted array: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}

代码解释

  1. 初始化:数组 arr 是待排序的数组,n 是数组的长度。
  2. 步长控制:使用 for 循环控制步长,从 n/2 开始,每次步长减半,直到步长为1。
  3. 主排序循环
    • 从当前步长位置开始遍历数组。
    • 将当前元素存入 temp
    • 使用内部 for 循环进行插入排序,步长为当前的 gap,如果前面的元素比 temp 大,则将其向后移动一位,直到找到合适的位置插入 temp
  4. 结果输出:排序完成后,输出排序后的数组。

示例运行

假设输入数组为 {12, 34, 54, 2, 3}

  1. 初始状态:{12, 34, 54, 2, 3}
  2. 第一次步长为2:
    • 插入 54 和 2:{12, 2, 54, 34, 3}
    • 插入 34 和 3:{12, 2, 3, 34, 54}
  3. 第二次步长为1:
    • 插入排序:{2, 3, 12, 34, 54}

最终结果:{2, 3, 12, 34, 54}

时间复杂度分析

希尔排序的时间复杂度依赖于步长序列的选择,常见的步长序列有Knuth序列、Hibbard序列等。一般情况下,时间复杂度难以精确分析,但有以下结论:

  • 最优时间复杂度:当使用最佳步长序列时,希尔排序的时间复杂度可以达到 O(nlog2n)O(n \log^2 n)
  • 最坏时间复杂度:在最坏情况下,时间复杂度为 O(n2)O(n^2),这取决于步长序列的选择。
  • 平均时间复杂度:通常情况下,希尔排序的平均时间复杂度优于插入排序,约为 O(nlogn)O(n \log n)O(nlog2n)O(n \log^2 n) 之间。

空间复杂度分析

希尔排序是一种原地排序算法,不需要额外的辅助空间,空间复杂度为 O(1)O(1)。在本实现中,我们仅使用了几个额外的变量(gap, i, temp, j),因此空间复杂度保持为常数级别。

优缺点

优点

  • 简单易实现。
  • 比插入排序效率高,特别是对于大规模数据。
  • 原地排序,空间复杂度低。
  • 对于大多数实际应用,性能较好。

缺点

  • 最坏情况下时间复杂度仍然可能较高。
  • 不稳定排序算法,可能改变相同元素的相对顺序。

适用场景

希尔排序适用于以下场景:

  • 数据量较大的排序。
  • 需要一个比插入排序更高效的排序算法时。
  • 对稳定性要求不高的场景。

总结

希尔排序是一种高效的插入排序改进算法,通过使用步长序列来减少元素移动次数,从而提高排序效率。尽管其最坏情况下的时间复杂度仍然可能较高,但在大多数实际应用中,希尔排序的性能优于插入排序,特别是对于大规模数据。