说起希尔排序,那不得先说说直接插入排序,插入排序的基本思想是:将记录插入到有序的列队中。比如有一组记录{1,9,10,13},将一个新的记录5插入后是{1,5,9,10,13}。简单分析下,插入的过程是从原来记录的尾部开始和待插入记录进行比较,如果比较失败则移动当前比较的记录,直到比较成功,将记录插入。注意是边比较边移动。来看代码:
//直接插入
void main()
{
int arr[10] = {1,4,5,7,3,8,2,9,6,0};
int len = sizeof(arr)/sizeof(arr[0]);
int j;
for(int i=1;i<len;i++)
{
int temp = arr[i];
j = i-1;
//不断比较,移动的过程
while(j>-1 && arr[j]>temp)
{
arr[j+1] = arr[j];
j--;
}
arr[j+1] = temp;
}
}
为什么要对插入排序改进呢,插入排序的时间复杂度O(n*2)。它主要对已经排好序的数据操作时效率高,而且每次只能将数据移动一位。来看看希尔排序是怎么实现的。
希尔排序是将待排序的记录进行分组,将大的待排序列分成若干子序列,然后子序列进行直接插入排序,当整个序列基本有序时,再对全体记录进行一次直接插入排序。
基本有序概念:就是小的关键字基本在前面,大的基本在后面,不大不小的基本在中间。要满足此基本有序,采取的是跳跃分割的策略。
跳跃分割:将相距某个"增量"的记录组成一个子序列,保证在子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序 。
来简单看下过程:
代码附上:
//希尔排序
void swapInt(int * a,int*b)
{
int c=*a;
*a=*b;
*b=c;
}
void shell(int*data,unsigned int len)
{
if(len<=1||data==NULL) return;
for(int div=len/2;div>=1;div=div/2)//定增量div,并不断减小
{
for(int i=0;i<div;++i)//分组成div组
{
for(int j=i;j<len-div;j+=div)//对每组进行插入排序
{
for(int k=j+div;k<len;k+=div)
{
if(data[j]>data[k])
swapInt(data+j,data+k);//交换两个数的值
}
}
}
}
}
int main()
{
int arr[] = {1,4,5,7,3,8,2,9,6,0};
int len = sizeof(arr)/sizeof(arr[0]);
shell(arr,len);
myPrint(arr,len);
}
希尔排序的时间复杂度大约是O(n1.5),需要注意的是增量序列的最后一个增量值必须等于1。由于其实跳跃式的移动,所以希尔排序不稳定。