排序
将一个数据元素的任意序列,重新排列成一个按关键字有序的序列。
#define MAXSIZE 20 //一个用作示例的小顺序表的最大长度
typedef int KeyType; //定义关键字类型为整型
typedef struct{
KeyType key; //关键字项
InfoType otherinfo; //其他数据项
}RedType; //记录类型
typedef struct{
RedType r[ MAXSIZE + 1 ]; //r[0]闲置或用作哨兵单元
int length; //顺序表长度
}SqList; //顺序表类型
1 插入排序
(1) 直接插入排序
它是将一个记录插入到以排好序的有序表中,从而得到一个新的、记录数增1的有序表。
void
InsertSort( SqList &L ){
int i,j;
for( i = 2; i <= L.length; ++i ){
if( L.r[ i ].key < L.r[ i - 1 ].key ){ //若<,需将L.r[ i ]插入有序子表
L.r[ 0 ] = L.r[ i ]; //复制为哨兵
for( j = i - 1;L.r[ 0 ].key < L.r[ j ].key; --j ){
L.r[ j + 1 ] = L.r[ j ]; //记录后移
}
L.r[ j + 1 ] = L.r[ 0 ]; //插入到正确位置
}
}
}
(2) 二分插入排序
由于插入排序的基本操作是在一个有序表中进行查找和插入,这个查找操作可利用二分查找来实现。
void
BinsertSort( SqList &L ){
for( i = 2; i <= L.length; ++i ){ //依次插入第2-第n个元素
L.r[ 0 ] = L.r[ i ]; //将当前插入元素存到哨兵位置
low = 1;
high = i - 1; //采用二分查找法查找插入位置
while( low <= high ){
mid = ( low + high ) / 2;
if( L.r[ 0 ].key < L.r[ mid ].key )
high = mid -1;
else
low = mid + 1;
} //循环结束,high+1则为插入位置
for( j = i - 1; j >= high + 1; --j )
L.r[ j + 1 ] = L.r[ j ]; //移动元素
L.r[ high + 1 ] = L.r[ 0 ]; //插入到正确位置
}
}
(3) 希尔排序
先将整个待排记录列表分割成若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
希儿排序的一个特点是:子序列的构成不是简单的“逐段分割”,而是将相隔某个“增量”的记录组成的一个子序列。
void
ShellInsert( SqList &L, int dk ){
//对顺序表L进行一趟增量为dk的shell排序,dk为步长因子
for( i = dk + 1; i <= L.length; ++i )
if( L.r[ i ].key < L.r[ i - dk ].key ){
L.r[ 0 ] = L.r[ i ];
for( j = i -dk; j > 0 && L.r[ 0 ].key < L.r[ j].key );
j = 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]的插入排序
}
2 交换排序
(1)冒泡排序
先将第一个记录的关键字和第二个记录的关键字进行比较,若为逆序,则将两个记录交换之,然后比较第二个记录和第三个记录的关键字。依次类推。
void
bubble_sort( SqList &L ){ //冒泡排序算法
int m,i,j;
RedType x; //交换时临时存储
for( m = 1; m < n - 1; m++ ){ //总共需m趟
for( j = 1; j < n - m; j++ )
if( L.r[ j ].key > L.r[ j + 1 ].key ){ //发生逆序
x = L.r[ j ];
L.r[ j ] = L.r[ j + 1 ];
L.r[ j + 1 ] = x; //交换
}
}
}
void
bubble_sort( SqList &L ){ //改进的冒泡排序算法
int m,i,j;
flag = 1; //flag作为是否有交换的标记
RedType x; //交换时临时存储
for( m = 1; m < n - 1 && flag == 1; m++ ){ //总共需m趟
flag = 0;
for( j = 1; j <= m; j++ )
if( L.r[ j ].key > L.r[ j + 1 ].key ){ //发生逆序
flag = 1; //发生交换,flag置为1,若本趟没发生交换,flag保持0
x = L.r[ j ];
L.r[ j ] = L.r[ j + 1 ];
L.r[ j + 1 ] = x; //交换
}
}
}
(2)快速排序
首先任意选取一个记录(通常可选第一个记录)作为枢轴(或支点),然后按照下述原则重新排列其余记录:
将所有关键字较它小的记录都安置在它的位置之前,将所有关键字较大的记录都安置在它的位置之后。
void
main(){
Qsort( L, 1, L.length );
}
void
Qsort( SqList &L, int low, int high ){
//对顺序表L快速排序
if( low < high ){ //表长大于1
//将L一分为2,pivotloc为枢轴元素排好序的位置
pivotloc = partition( L, low, high );
Qsort( L, low, pivotloc - 1 ); //对低子表递归排序
Qsort( L, pivotloc + 1, high ); //对高子表递归排序
}
}
int
partition( SqList &L, int low, int high ){
L.r[ 0 ] = L.r[ low ]; //用子表的第一个记录作枢轴记录
pivotkey = L.r[ low ].key; //枢轴记录关键字
while( low < high ){ //从表的两端交替的向中间扫描
while( low < high && L.r[ high ].key >= pivotkey )
--high; //将比枢轴记录小的记录移到低端
L.r[ low ] = L.r[ high ];
while( low < high ){
while( low < high && L.r[ high ].key <= pivotkey )
++low; //将比枢轴记录大的记录移到高端
L.r[ high ] = L.r[ low ];
}
L.r[ low ] = L.r[ 0 ]; //枢轴记录到位
return low; //返回枢轴位置
}
3 选择排序
(1) 简单选择排序
void
SelectSort( SqList &L ){
for( i = 1; i < L.length; ++i ){
k = i;
for( j = i + 1; j <= L.length; j++ )
if( L.r[ j ].key < L.r[ k ].key )
k = j; //记录最小值位置
if( k != i )
L.r[ i ] = L.r[ k ]; //交换
}
}
(2) 堆排序
堆的含义表明,完全二叉树中所有非终端结点的值均不大于其左右孩子结点的值。若在输出堆顶的最小值后,使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素中的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。
从一个无序序列建堆的过程就是一个反复“筛选”的过程。
void
HeapAdjust( elem R[], int s, int m ){
//已知E[s...m]中记录的关键字除R[s]之外均满足堆的定义,本函数调整R[s]的关键字,使R[s...m]成为一个大根堆。
rc = R[s];
for( j = 2 * s; j <= m; j *= 2 ){ //沿key较大的孩子结点向下筛选
if( j < m && R[ j ] < R[ j + 1 ] )
++j; //j为key较大的记录的下标
if( rc >= R[ j ] )
break;
R[ s ] = R[ j ];
s = j; //rc应插入在位置s上
}
R[ s ] = rc; //插入
}
void
HeapSort( elem R[] ){
//对R[1]到R[n]进行堆排序
int i;
for( i = n / 2; i >= 1; i-- )
HeapAdjust( R, i, n ); //建初始堆
for( i = n; i > 1; i-- ){ //进行n-1趟排序
Swap( R[ 1 ], R[ i ] ); //根与最后一个元素交换
HeapAdjust( R, 1, i - 1 ); //对R[1]到R[i-1]重新建堆
}
}
4 归并排序
归并的含义是将2个或2个以上的有序表组合成一个新的有序表。
假设初始序列含有n个记录,则可看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2个长度为2或1的有序子序列,再两两归并,重复。,直至得到一个长度为n的有序列表为止,这种排序方法称为2-路归并排序。