希尔排序:缩小增量排序,属于插入排序的一种,从前面的直接插入时间分析可知,其时间复杂度为O(n^2),若待排序的记录基本有序,其时间复杂度可以提高至O(n)
基本思想:先将整个待排序的记录序列分割为若干个子序列分别进行直接插入排序,待整个序列基本有序时,再对全体记录进行一次直接插入排序
特点:1)子序列不是简单的分段,而是相隔某个增量的记录组成一个子序列
2)增量务必是素数或者说质数,而且最后一个增量一定是1,才能完成一趟完整的排序
看如下的shell排序算法:
int ShellInsert(MergeType* L, int dk) //希尔排序
{
int nCompKey;
int j = -1;
for (int i = dk; i < L->len; i++ )
{
if (L->elem[i] < L->elem[i-dk])
{
nCompKey = L->elem[i];
for (j = i-dk; j >= 0; j -= dk)
{
if (nCompKey >= L->elem[j])
{
break;
}
L->elem[j+dk] = L->elem[j];
}
L->elem[j+dk] = nCompKey;
}
}
return 0;
}
这里只是在比较的时候不是直接插入比较的时候使用的增量为1,而是dk,其他都是一样的,如下是几趟dk的算法:
int ShellSort(MergeType* L, int* dlta, int len)
{
if (!L->elem)
{
return -1;
}
for (int i = 0; i != len; i++)
{
ShellInsert(L, dlta[i]);
}
return 0;
}
算法的时间复杂性:当增量序列为dlta[k] = 2^(t-k+1)-1时,希尔排序的时间复杂度为O(n^1.5),总之时间排序的复杂度优于直接插入排序
测试程序如下,其他函数定义详见:冒泡算法的改进
#define LISTLEN 10
MergeType pList;
pList.elem = (int*)malloc(sizeof(int)*LISTLEN);
pList.len = LISTLEN;
pList.size = LISTLEN;
ScanfList(&pList);
//希尔排序
int nDlta[] = {5, 3, 1};
ShellSort(&pList, nDlta, ARRLEN(nDlta));
PrintList(&pList);
free(pList.elem);
free(pT.elem);
pList.elem = NULL;
pT.elem = NULL;