3.1、排序的分类
3.1.1、排序的分类
根据排序过程所用策略的不同,可将内部排序方法分为5类:交换排序、选择排序、插入排序、归并排序和基数排序。其中交换排序、选择排序和插入排序是一个逐步扩大记录的有序序列长度的过程。
3.2、直接插入排序
算法思想:每次将无序区的第一个记录按关键字插入到有序区的合适位置,并将有序区的长度加1. 假设记录个数为8,输入关键字序列为(56,68,25,45,90,38,10,72),每一趟插入排序的结果如图所示
第i躺插入排序若需将记录L.rcd[i+1]插入到有序区L.rcd[1…i]中,则应先执行以下步骤
void InsertSort(RcdSqList &L){//对顺序表L作直接插入排序
int i,j;
for(i=1;i<L.length;i++)
if(L.rcd[i+1].key<L.rcd[i].key){//需将L.rcd[i+1]插入到有序序列中
L.rcd[0]=L.rcd[i+1];//先将记录L.rcd[i+1]保存在空闲的0号单元
j=i+1;
do{//记录后移
j--;
L.[j+1]=L.rcd[j];
}while(L.rcd[0].key<L.rcd[j-1].key);//判断是否需要继续后移
}
L.rcd[j]=L.rcd[0];//插入
}
3.3、希尔排序
希尔排序的思路:
希尔排序的演示:
希尔排序与直接插入排序的差别:
一趟希尔排序:
void ShellInsert(RcdSqList & L,int dk){
//对顺序表L作一趟希尔排序,增量为dk
int i,j;
for(i=1;i<=L.length-dk;i++)
if(L.rcd[i].key>L.rcd[i+dk].key){
//需将L.rcd[i+dk]插入到有序序列
L.rcd[0]=L.rcd[i+dk];//暂存再L.rcd[0]
j=i+dk;
do{//记录后移
j-=dk;
L.rcd[j+dk]=L.rcd[j];
}while(j-dk>0&&L.rcd[0].key<L.rcd[j-dk].key);//是否继续移动
L.rcd[j]=L.rcd[0];//插入
}
}
希尔排序:
void ShellSort(RcdSqList & L,int d[],int t){
//按增量序列d[0..t-1]对顺序表L作希尔排序
int k;
for(k=0;k<t;k++)
ShellSort(L,d[k]);
}
希尔排序的时间复杂度是所取增量序列的函数,尚难准确分析。且希尔排序是不稳定的排序,如(56,34,47,23,66,18,47),若排序结果为(18,23,34,47,47,56,66)则称该排序方法是稳定的;若排序结果为(18,23,34,47,47,56,66)则称该排序方法是不稳定的。
3.4、基数排序
基数排序的数据类型定义如下:
typedef struct{
KeysType * keys;//关键字
...//其他数据项
}KeysRcdType;//基数排序中的记录类型
typedef struct{
KeysRcdType *rcd;//0号位置放哨兵
int length;//顺序表长度
int size;//顺序表容量
int digitNum;//关键字位数
int radix;//关键字基数
}KeysSqList;//顺序表类型
基于count数组,依次计算关键字从0到9的记录的起始位置,存入数组pos。值为0的关键字的起始位置为1,值为j(1<=j<=9)的关键字的起始位置是值为j-1的关键字的起始位置加上值为j-1的关键字的个数,如图所示
第一趟收集时由337的个位数7取得器起始位置pos[7]的值6,将337放入rcd1[6]中,并将pos[7]加1,令pos[7]指向下一个个位数为7的记录应放入的位置。接着,由332的个位数2取得其起始位置pos[2]的值为2,将332放入rcd1[2]中,并将pos[2]加1.
继续上述过程,第一趟收集的结果如下图所示:
收集的代码如下:
for(k=1;k<=n;k++){//第i躺收集
j=rcd[k].keys[i];
rcd1[pos[j]++]=rcd[k];//复制记录,对应的起始位置加1
}
第三趟对百位数进行分配和收集,将记录从数组rcd收集到数组rcd1,分配和收集的结果分别如下图所示。第三趟收集的结果就是最后排序的结果。由于趟数为奇数,须将数组rcd1复制回rcd。
计数基数排序:
Status RadixSort(KeysSqList &L){//对顺序表L进行计数基数排序
KeysRcdType *rcd1;//开设同等大小的辅助空间用于复制数据
int i=0,j;
int *count,*pos;
count=(int *)malloc(L.radix*sizeof(int));
pos=(int *)malloc(L.radix*sizeof(int));
rcd1=(KeysRcdType *)malloc((L.length+1)*sizeof(KeysRcdType));//
if(NULL==count||NULL==pos||NULL==rcd1)
return OVERFLOW;
while(i<L.digitNum){
for(j=0;j<L.radix;++j)
count[j]=0;
if(0==i%2)//对L.rcd进行一趟基数排序,结果存入rcd1
RadixPass(L.rcd,rcd1,L.length.i++,count,pos,L.radix);
else//对rcd1进行一趟基数排序,结果存入L.rcd
RadixPass(rcd1,L.rcd,L.length.i++,count,pos,L.radix);
}
if(1==L.digitNum%2)//排序后的结果在rcd1中,复制至L.rcd中
for(j=1;j<=L.length;j++)
L.rcd[j]=rcd1[j];
free(count);
free(pos);
free(rcd1);
return OK;
}
一趟计数基数排序:
void RadixPass(KeysRcdType rcd[],KeysRcdType rcd1[],int n,int i,int count[],int pos[],int radix){
//对数组rcd中记录关键字的第i位计数,计算得到起始位置数组pos[]
//并按照pos数组将数组rcd中记录复制到数组rcd1中
int k,j;
for(k=1;k<=n;k++)//对各种取值计数
count[rcd[k].keys[i]]++
pos[0]=1;
for(j=1;j<radix;j++)//求起始位置
pos[j]=count[j-1]+pos[j-1];
for(k=1;k<=n;k++){//收集
j=rcd[k].key[i];
rcd1[pos[j]++]=rcd[k];
}
}