目录
基本概念
希尔排序是D.L.Shell于1959年提出来的一种排序算法,在这之前排序算法的时间复杂度基本上是O(n^2)的,希尔排序算法是突破这个时间复杂度的第一批算法之一。
我们前一节讲的直接插入排序,应该说,它的效率在某些时候是很高的,比如,我们的记录本身就是基本有序的,我们只需要少量的插入操作,就可以完成整个记录集的排序工作,此时直接插入很高效。还有就是记录数比较少时,直接插入的优势也比较明显。可问题在于,两个条件本身就过于苛刻,现实中记录少或者基本有序都属于特殊情况。
如何让待排序的记录个数较少呢?很容易想到的就是将原本有大量记录数的记录进行分组。分割成若干个子序列,此时每个子序列待排序的记录个数就比较少了,然后在这些子序列内分别进行直接插入排序,当整个序列都基本有序时,注意只是基本有序时,再对全体记录进行一次直接插入排序。
如何是整个序列基本有序呢?我们需要采取跳跃分割的策略:将相距某个“增量”的记录组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序。
代码示例
using System;
class ShellSort
{
public static void Sort(int[] arr)
{
int n = arr.Length;
// 使用希尔增量序列来进行分组
for (int gap = n / 2; gap > 0; gap /= 2)
{
// 对每个分组进行插入排序
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[] arr = { 5, 3, 8, 6, 2, 7, 1, 4 };
Console.WriteLine("Array before sorting:");
PrintArray(arr);
Sort(arr);
Console.WriteLine("Array after sorting:");
PrintArray(arr);
}
// 打印数组
private static void PrintArray(int[] arr)
{
foreach (int num in arr)
{
Console.Write(num + " ");
}
Console.WriteLine();
}
}
希尔排序的分析
-
时间复杂度:
- 最坏情况下,希尔排序的时间复杂度为O(n^2),其中n是待排序数组的大小。在最坏情况下,需要进行多次插入排序,每次的增量为1,类似于直接插入排序。
- 平均情况下,希尔排序的时间复杂度是由增量序列决定的。尽管希尔排序的理论时间复杂度仍然是一个开放问题,但它的平均时间复杂度较好的增量序列通常可以达到O(nlogn)的级别。
-
空间复杂度:
- 希尔排序是原地排序算法,只需要使用常量级别的额外空间来存储临时变量。
- 因此,希尔排序的空间复杂度为O(1)。
-
稳定性:
- 希尔排序是不稳定的排序算法。在插入排序的过程中,由于跳跃式的移动元素,可能会改变相同元素之间的相对顺序。