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+; --j)
L.r[j+1] = L.r[j];//移动元素
L.r[high+1] = L.r[0];//插入到正确位置
}// BInsertSort
}
当n较大时,总关键码比较次数比直接插入排序的最坏情况要好得多,但比其最好情况要差;
在对象的初始排列已经按关键码排好序或接近有序时,直接插入排序比折半插入排序执行的关键码比较次数要少;
3)希尔排序
先将整个待排记录序列分割成若干子序列,分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
一一
希尔排序算法,特点:
1)缩小增量
2)多遍插入排序
1.定义增量序列Dk : DM>DM-1>.….>D1=1
·刚才的例子中:D3=5,D2=3,D1=1
2.对每个Dk进行“Dk-间隔”插入排序(k=M,M-1,...1)
void ShellSort (Sqlist&L, int dlta[], int t){
//按增量序列dlta[0..t-1]对顺序表L作希尔排序。
for(k=0; k<t; ++k)
Shelllnsert(L,dlta[k]);//一趟增量为dlta[k]的插入排序
}//ShellSort
void Shelllnsert(SqList &L, int dk){
for(i=dk+1; i<=L.length; ++i)
if(r[i].key < r[i-dk].key) {
r[0]=r[i];
for(j=i-dk; j>0 &&(r[0].key<r[j].key); j=j-dk)
r[j+dk]=r[j];
r[j+dk]=r[0];
}
}
2、交换排序
1)冒泡
n个记录,总共需要n-1趟
第m趟需要比较n-m次
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[i].key>L.r[j+1].key){
//发生逆序
x=L.r[j];
L.r[j]=L.r[j+1];
L.r[j+1]=x;//交换
}//endif
}//for
}
2)快速排序
·任取一个元素(如:第一个)为中心
·所有比它小的元素一律前放,比它大的元素一律后放,形成左右两个子表;
·对各子表重新选择中心元素并依此规则调整
①每一趟的子表的形成是采用从两头向中间交替式逼近法;
②由于每趟中对各子表的操作都相似,可采用递归算法。
void QSort (SqList &L, int low, int high){
//对顺序表L快速排序
if (low < high){
//长度大于1
pivotloc = Partition(L, low, high);
//将L.r[low..high]一分为二,pivotloc为枢轴元素排好序的位置
QSort(L, low, pivotloc-1);//对低子表递归排序
QSort(L, pivotloc+1, high);//对高子表递归排序
}//endif
}// QSort
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 && L.r[low].key <= pivotkey ) ++low;
L.r[high] = L.r[low];
}
L.r[low]=L.r[0];
return low;
}
3、选择排序
1)简单选择排序
void SelectSort(SqList &K) {
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个元素的次小值(次大值)...如此反复,便能得到一个有序序列,这个过程称之为堆排序。
小根堆:
1.输出堆顶元素之后,以堆中最后一个元素替代之;
2.然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换;
3.重复上述操作,直至叶子结点,将得到新的堆,称这个从堆顶至叶子的调整过程为“筛选”
void HeapAdjust (elem R[ ], int s, int m) {
/*已知R[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上
}//for
R[s] = rc;//插入
}// HeapAdjust
对一个无序序列反复“筛选”就可以得到一个堆;
即:从一个无序序列建堆的过程就是一个反复“筛选”的过程。
单结点的二叉树是堆;
在完全二叉树中所有以叶子结点(序号i > n/2)为根的子树是堆。
这样,我们只需依次将以序号为n/2,n/2 - 1,...…1的结点为根的子树均调整为堆即可。
将初始无序的R[1]到R[n]建成一个小根堆,可用以下语句实现:
for ( i = n/2 ; i >= 1 ; i - - )
HeapAdjust ( R , i,n );
若对一个无序序列建堆,然后输出根;重复该过程就可以由一个无需序列输出有序序列。
实质上,堆排序就是利用完全二叉树中父结点孩子结点之间的内在关系来排序的。
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]重新建堆
}
}//HeapSort