插入排序
插入排序,就是将从首位元素起的每一个元素插入他在已排好序的序列的正确位置,通过遍历我们可以找到他应该存储在哪个位置,将比他大的元素后移后,把他填入该位置。
图示算法
代码实现
void InsertSort(SqList &L)
{ // 对顺序表L作直接插入排序
int i,j;
for(i=1;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]; // 插入到正确位置
}
}
希尔排序
希尔排序是将数组元素间隔分组,对在同一组的元素比大小,将同组元素按大小关系排序。例如dlta数组存储{5,2,1},第一轮排序就将间隔为5的元素提取出来分组,进行排序,第二轮提取间隔为2的元素分组,进行排序,以此类推。
图示算法
代码实现
void ShellInsert(SqList &L,int dk)
{ // 对顺序表L作一趟希尔插入排序。
int i,j;
for(i=1+dk;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[0].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作希尔排序。算法10.5
int k;
for(k=0;k<t;++k)
{
ShellInsert(L,dlta[k]); // 一趟增量为dlta[k]的插入排序
printf("第%d趟排序结果:\n",k+1);
print(L);
}
}
快速排序
快速排序是最常用,类似于归并排序,用的是分而治之的思想,可以大大简化排序的重复遍历过程,基本思想是先取一个值作为枢轴,用于与遍历得到的值进行比较,找出左端大于枢轴的值,交换到右端,找出右端小于枢轴的值,交换到左端。
图示算法
代码实现
int Partition(SqList &L,int low,int high)
{ // 交换顺序表L中子表r[low..high]的记录,枢轴记录到位,并返回其
// 所在位置,此时在它之前(后)的记录均不大(小)于它。算法10.6(b)
int pivotkey;
L.r[0]= L.r[low];
pivotkey=L.r[low].key;
printf("%4d",pivotkey);
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;
}
void QSort(SqList &L,int low,int high)
{ // 对顺序表L中的子序列L.r[low..high]作快速排序。算法10.7
int pivotloc;
if(low<high)
{ // 长度大于1
pivotloc=Partition(L,low,high); // 将L.r[low..high]一分为二
QSort(L,low,pivotloc-1); // 对低子表递归排序,pivotloc是枢轴位置
QSort(L,pivotloc+1,high); // 对高子表递归排序
}
}
void QuickSort(SqList &L)
{ // 对顺序表L作快速排序。算法10.8
QSort(L,1,L.length);
}
选择排序
选择排序,顾名思义,就是选择出值,插入到合适的位置,以选择最小值排序为例,选择排序流程就是从表中遍历找到最小值以及其所在的位置,将他与当前排序到的位置的元素交换。
图示算法
代码实现
void SelectSort(SqList &L){
for(int i=1;i<L.length;i++){
j=SelectMinKey(L,i);
if(i!=j){
int t=L.r[i];
L.r[i]=L.r[j];
L.r[j]=t;
}
}
堆排序(树形选择排序)
图示算法
如图所示的是两种堆的类型,我们可以通过堆排序算法将一个乱序堆构建成排序堆,值得注意的是堆排序算法的时间复杂度是nlog2n,最坏情况的时间复杂度也是这个,堆是一个完全二叉树。
代码实现
typedef SqList HeapType; // 堆采用顺序表存储表示
void HeapAdjust(HeapType &H,int s,int m) // 算法10.10
{ // 已知H.r[s..m]中记录的关键字除H.r[s].key之外均满足堆的定义,本函数
// 调整H.r[s]的关键字,使H.r[s..m]成为一个大顶堆(对其中记录的关键字而言)
RedType rc;
int j;
rc=H.r[s];
for(j= 2*s; j<=m; j*=2)
{
if(j<m && LT(H.r[j].key,H.r[j+1].key))
++j; // j为key较大的记录的下标
if(!LT(rc.key,H.r[j].key))
break; // rc应插入在位置s上
H.r[s]=H.r[j];
s=j ;
}
H.r[s]=rc; // 插入
}
void HeapSort(HeapType &H)
{ // 对顺序表H进行堆排序。算法10.11
RedType t;
int i;
for(i=H.length/2; i>0 ; --i ) // 把H.r[1..H.length]建成大顶堆
HeapAdjust(H,i,H.length);
for( i= H.length; i>1 ; --i )
{
t=H.r[1];
H.r[1]=H.r[i];
H.r[i]=t;
HeapAdjust(H,1,i-1); // 将H.r[1..i-1]重新调整为大顶堆
}
}