一、希尔排序
(1)原理
希尔排序(Shell Sort),是一种对直接插入排序改进后增加效率的一种排序算法。如何让排序算法记录数据个数较少呢?很容易想到的就是将原本有大量记录数的记录进行分组。分割成若干个子序列,此时每个子序列待排序的记录个数就比较少了,然后在这些子序列内分别进行直接插入排序,当整个序列都基本有序的时候,注意只是基本有序,然后再对全体记录进行一次直接插入排序。
注意:所谓的基本有序就是小的关键字基本在前面,大的基本在后面,不大不小的基本在中间,像{2,1,3,6,4,7,5,8,9}这样可以称为基本有序了。但像{1,5,9,3,7,8,2,4,6}这样的9在第三位,2在倒数第三位就谈不上基本有序。
如上面这样分完组后就各自排序的方法达不到我们要求。因此,我们需要采取跳跃分割的策略:将相距某个“增量”的记录组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序。
(2)代码实现
void ShellSort(SqList *L) //L为顺序表,进行排序
{
int i,j,k=0;
int increment=L->length;
do
{
increment=increment/3+1 //增量序列
for(i=increment+1;i<=L->length;i++)
{
if(L->r[i]<L->r[i-increment]) //需将L->r[i]插入有序增量子表
{
L->r[0]=L->r[i]; //暂时存在L->r[0]
for(j=i-increment;j>0&&L->r[0]<L->r[j];j-=increment)
L->r[j+increment]=L->r[j]; //查找插入位置
L->r[j+increment]=L->r[0]; //插入
}
}
}
while(increment>1);
}
比如我们现在的序列是{9,1,5,8,3,7,4,6,2},现在将他分为三组{9,5,1},{8,3,7},{4,6,2},将其各自排序后,变成了{1,5,9},{3,7,8},{2,4,6},再合并为{1,5,9,3,7,8,2,4,6}。
这个算法的时间复杂度为O(nlongn)。
二、快速排序
(1)基本思想
快速排序(Quick Sort)通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一个部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
(2)代码实现
void quicksort(list *L)
{
Qsort(L,1,L->length);
}
//对顺序表L中的子序列L->r[low ~ high]进行快速排序
void Qsort(list *L,int low,int high)
{
int pivot;
if(low<high)
{
//一分为二,算出中间值
pivot=Partition(L,low,high);
//选取当中的一个关键字,使他左边的值都比他小,右边的比它大
Qsort(L,low,pivot-1);
Qsort(L,pivot+1,high);
}
}
//快排
int Partition(list *L,int low,int high)
{
int data;
data=L->r[low]; //将第一个数设置为中间值进行排序
while(low<high)
{
while(low<high&&L->r[high]>=data)
high--;
swap(L,low,high); //比中间值小的放到低端,交换值
while(low<high&&L->r[low]<=data)
low++;
swap(L,low,high);
}
return low; //返回中间值所在位置
}
快排的时间复杂度,最优的情况下时间复杂度为O(nlogn)。
(3)快排的优化
如果我们将关键字选取为整个序列的中间位置,那么我们可以将整个序列分成小数集合和大数集合了。三数取中(取三个关键字先进行排序,将中间数作为枢轴,一般是取左端、右端和中间三个数,也可以随机选取)。
int Partition(list *L,int low,int high)
{
int data,m;
m=low+(high-low)/2;
if(L->r[low]>L->r[high])
swap(L,low,high); //交换左右端数据,保证左端较小
if(L->r[m]>L->r[high])
swap(L,high,m); //交换与右端数据,保证中间较小
if(L->r[m]>L->r[low])
swap(L,m,low); //交换中间与左端数据,保证左端较小
data=L->r[low];
L->r[0]=data; //将枢轴数据备份到L->r[0]
while(low<high)
{
while(low<high&&L->r[high]>=data)
high--;
L->r[low]=L->r[high];
while(low<high&&L->r[low]<=data)
low++;
L->r[high]=L->r[low];
}
L->r[low]=L->r[0];
return low; //返回中间值所在位置
}