待排记录的数据类型:
typedef struct{
KeyType key; //关键字
InfoType otherinfo;//其他数据项
}RedType;//记录类型
typedef struct{
RedType r[MAXSIZE+1];//r[0]闲置或用做哨兵单元
int length;//顺序表长度
}SqList;//顺序表类型
1.直接插入排序
直接插入排序是将一个记录插入到已排好有序表中,从而得到一个新的、记录数增1的有序表。
例如:已知待排序的一组记录的初始排列如下:
R(49),R(38),R(65),R(97),R(76),R(13),R(27),R(
49),……
假设在排序过程中,前4个记录已按关键字递增的次序重新排序,构成一个含有4个记录的有序序列:
{R(38),R(49),R(65),R(97)}
现在把地5个记录插入上述序列,以得到一个新的含5个记录(R(76))的有序序列,则首先要在上述序列中进行查找以确定R(76)应插入的位置,然后进行插入。假设从R(97)起向左进行查找,由于65<76<97,则R(76)应插入在R(65)和R(97)之间,从而得到下列新的有序序列:
{R(38),R(49),R(65),R(76),R(97)}
一般情况下,第i趟直接插入排序的操作为:在含有i-1个记录的有序子序列r[1..i-1]中插入一个记录r[i]后,变成含有i个记录的有序子序列r[1..i];为了在查找插入位置的过程中避免数组
下标出界,在r[0]处设置监听哨。在自i-1起往前搜索的过程中,可以同时后移。
void InsertSort(SqList &L){
//对顺序表L作直接插入排序
for(i=2;i<=L.length;++i)
if(L.r[i].key<L.r[i-1].key) //若待插入关键字小于前面的关键字,则需要在前面序列插入
{
L.r[0]=L.r[i]; //复制为哨兵
L.r[i]=L.r[i-1];
for(j=i-2;L.r[0].key<L.r[j].key;--j)
L.r[j+1]=L.r[j]; //记录后移
L.r[j+1]=L.r[0]; //插入到正确位置
}
时间复制度为O(n^2)。
2.折半插入排序
区别在于找插入位置的方法不一样(用折半查找法找插入位置),其他一样。
具体代码:
void BInsertSort(SqList &L){
//对顺序表L作折半插入排序
for(i=2;i<=L.Length;++i)
{
L.r[0]=L.r[i]; //将L.r[i]暂存到L.r[0]
low=1; high=i-1;
while(low<=high) //在r[low..high]中折半查找有序插入位置
{
m=(low+high)/2; //折半
if(L.r[0].key<L.[m].key)
high=m-1; //插入点在低半区
else
low=m+1; //插入点在高半区
}
for(j=i-1;j<=high+1;--j) //记录后移
L.r[j+1]=L.r[j];
L.r[high+1]=L.r[0]; //插入
}
}
3.希尔排序
基本思想:先将整个待排记录列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
从图中操作可以发现:子序列的构成不是简单地“逐段分割”,而是将相隔某个“增量”的记录组成一个子序列,如上图例子中,第一趟排序时增量为5,第二趟排序时增量为3,
由于在前两趟的插入排序中记录的关键字是和同一子序列中的前一个记录的关键字进行比较,因此关键字较小的记录就不是一步一步地往前移,而是跳跃式地往前移,只要作记
的少量比较和移动即可完成排序。
void ShellInsert(SqList &L,int dk)
{
//对顺序表L作一趟希尔插入排序。本算法是和一趟直接插入排序相比,作如下修改:
//1.前后记录位置的增量是dk,而不是1;
//2.r[0]只是暂存单元,不是哨兵。当j<=0时,插入位置已找到
for(i=dk+1;i<=L.length;i++)
if(L.r[i].key<L.r[i-dk].key) //需要将L.r[i]插入有序增量表
{
L.r[0]=L.r[i]; //暂存在L.r[0]
for(j=i-dk;j>0&&(L.r[o].key<L.r[j].key);j-=dk)
L.r[j+dk]=L.r[j]; //记录后移,查找插入位置
L.r[j+dk]=L.r[0]; //插入
}
}
void ShellSort(SqList &L;int dlta[],int t)
{
//按增量列dlta[0..t-1]对顺序表L作希尔排列
for(k=0;k<t;k++)
ShellInsert(L,dlta[k]); //一趟增量为dlta[k]的插入排序
}
附上完整程序:
#include<stdio.h>
#include<math.h>
#define MAXNUM 10
void main()
{
void shellSort(int array[],int n,int t);//t为排序趟数
int array[MAXNUM],i;
for(i=0;i<MAXNUM;i++)
scanf("%d",&array[i]);
shellSort(array,MAXNUM,int(log(MAXNUM+1)/log(2)));//排序趟数应为log2(n+1)的整数部分
for(i=0;i<MAXNUM;i++)
printf("%d ",array[i]);
printf("\n");
}
//根据当前增量进行插入排序
void shellInsert(int array[],int n,int dk)
{
int i,j,temp;
for(i=dk;i<n;i++)//分别向每组的有序区域插入
{
temp=array[i];
for(j=i-dk;(j>=i%dk)&&array[j]>temp;j-=dk)//比较与记录后移同时进行
array[j+dk]=array[j];
if(j!=i-dk)
array[j+dk]=temp;//插入
}
}
//计算Hibbard增量
int dkHibbard(int t,int k)
{
return int(pow(2,t-k+1)-1);
}
//希尔排序
void shellSort(int array[],int n,int t)
{
void shellInsert(int array[],int n,int dk);
int i;
for(i=1;i<=t;i++)
shellInsert(array,n,dkHibbard(t,i));
}
若查看详细介绍 点击打开链接