思想:先将整个待排记录序列分割成若干子序列,分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
说明:
(1)子序列的构成不是简单地“逐段分割”,而是将相隔某个增量dk的记录组成一个子序列,让增量dk逐趟缩短(例如依次取5,3,1),直到dk=1为止。由于这个原因,希尔排序也叫缩小增量排序。
(2)优点:让关键字值小的元素能很快前移,且序列若基本有序时,再用直接插入排序处理,时间效率会高很多。
(3)希尔排序的一个重要性质是,一个h(k)排序的文件保持它的h(k)排序性,否则该算法没有任何意义,因为前面各趟排序的结果会被后面各趟排序给打乱。
(4)增量排序的一种流行(但是不好)的选择是使用Shell建议的序列:h(t)=N/2,h(k)=h(k+1)/2。
(5)时间复杂度及稳定性分析:
1)对特定的待排序对象序列,可以准确地估算关键码的比较次数和对象移动次数。但想要弄清关键码比较次数和对象移动次数与增量选择之间的依赖关系,并给出完整的数学分析,还没有人能够做到。
2)希尔排序的时间复杂度小于O(N2),大于O(NlogN)。
3)希尔排序不稳定。
(6)希尔排序是算法非常简单且又具有极其复杂的分析的好例子。它的性能在实践中完全可以接受,即使是对于数以万计的N仍是如此。编程的简单特点使它成为对适度地大量的输入数据经常选用的算法。
#include <stdio.h>
void ShellSort(int arr[], int len)
{
if (!arr || len == 0)
{
return;
}
int h, i, j, tmp;
for (h = len / 2; h > 0; h /= 2)
{
for (i = h; i < len; i++)
{
tmp = arr[i];
for (j = i; j >= h; j -= h)
{
if (arr[j - h] > tmp)
{
arr[j] = arr[j - h];
}
else
{
break;
}
}
arr[j] = tmp;
}
}
return;
}
int main(void)
{
int arr[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
ShellSort(arr, 10);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
在Java中实现的二叉树结构