内部排序2 (快速排序、堆排序、基数排序)
1.快速排序
(1)基本思想
通过一趟排序,将待排序列以枢轴为标准划分成两部分,使其中一部分记录的关键字均比另一部分小,再分别对这两部分进行快速排序,以达到整个序列有序。通常取第一个记录的值为基准值,或称枢轴。
(2)一趟快速排序
在这里插入图片描述
(3)快速排序的算法
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; // 返回枢轴位置
}
(4)快速排序分析
a.最好的情形(左、右子区间的长度大致相等), 最好时间复杂度应为O(nlog2n)。
b.最坏的情形(每次能划分成两个子区间,但其中一个是空),快速排序的最坏时间复杂度为O(n2)。
c.对n较大的情况,它是平均速度最快的排序算法,但当n很小时,此方法往往比其他简单排序方法还要慢。
d.快速排序是不稳定的。
2.简单选择排序
(1)基本思想
通过 n-i 次关键字间的比较,从无序序列[i…n]的 n-i+1 记录中选出关键字最小的记录作为第i个记录。
(2)算法
void SelectSort (SqList &L)
{
for (i=1; i<L.length; ++i) { //选择第i小记录换到位置i
j = SelectMinKey( L, i ); //在L.r[i..L.length]中选择key最小的记录
if ( i != j )
{
L.r[0] = L.r[i];
L.r[i] = L.r[j];//交换
L.r[j] = L.r[0];
}
}
}
(3)简单选择排序的算法分析
a.由于存在不相邻元素之间的交换,所以简单选择排序是不稳定的。
b.时间复杂度为O(n^2).
3.堆排序
(1)基本思想
堆排序利用大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征进行排序。
步骤:
按堆的定义将待排序序列 r[1…n] 调整为大根堆(这个过程称为建初堆),交换 r[1] 和 r[n],则 r[n]为关键字最大的记录。将 r[1…n-1] 重新调整为堆,交换 r[1] 和 r[n-1] ,则 r[n-1] 为关键字次大的记录。循环n-1次,直到交换了r[1]和r[2]为止,得到了一个非递减的有序序列r[1…n]。
大根堆:ki ≥ k2i , ki ≥ k2i+1
小根堆: ki ≤ k2i , ki ≤ k2i+1
大根堆实例
(2)堆的排序算法
// 已知H.r[s..m]中记录的关键字除H.r[s].key之外均满足堆的定义,
// 调整H.r[s]的关键字,使H.r[s..m]成为一个大顶堆
void HeapAdjust(SqList &H, int s, int m)
{
rc = H.r[s]; // 暂存H.r[s]
for (j=2*s; j<=m; j*=2) //沿key较大的孩子结点向下筛选
{
if (j<m && H.r[j].key<H.r[j+1].key) ++j;//j为较大孩子的下标
if (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)
{ for(i=H.length/2;i>0;--i) //H.r[1..length]初始建堆
HeapAdjust(H,i,H.length);
for(i=H.length; i>1; --i)
{
H.r[1]<-> H.r[i]; // 将堆顶记录和最后一个记录相互交换
HeapAdjust(H,1,i-1); // 将H.R[1..i-1]重新调整为大根堆
}
(3)堆排序的效率分析
a.时间复杂度:最坏和最好情况下的时间复杂度为O(n*logn)
b.空间复杂度:只需要一个记录大小的辅助储存空间
c.堆排序是不稳定的。
d.只适用于顺序结构不适用于链式结构
4.归并排序
(1)归并排序的主要思想
将若干有序子序列逐步归并,最终得到一个有序序列。
整个归并排序只需要logn向上取整趟
(2)归并排序的算法
//将有序的SR[i..m]和SR[m+1..n]归并为有序的TR[i..n]
void Merge(RcdType SR[ ],RcdType &TR[ ],int i,int m,int n)
{
for(j=m+1,k=i; i<=m && j<=n;++k){ //将SR中记录由小到大并入TR
if (SR[i].key<SR[j].key) TR[k]=SR[i++];
else TR[k]=SR[j++];
}
if (i<=m)
{
for(k,i;k<=n&&i<=m;k++,i++) TR[k] = TR[i]; // 将剩余的SR[i..m]复制到TR
}
if (j<=n)
{
for(k,j;k<=n&&i<=n;k++,i++) TR[k] = TR[j];// 将剩余的SR[j..n]复制到TR
}
}//merge
void MSort(RcdType SR[ ],RcdType &TR1[ ],int s,int t) {
if ( s==t ) TR1[s] = SR[s]; // 只有一个记录
else {
m = (s+t)/2; //将SR[s..t] 平分为SR[s..m]SR[m+1..t]
MSort(SR,TR2,s,m) //递归地将SR[s..m]归并排序,结果放入TR2[s..m]
MSort(SR,TR2,m+1,t) //递归地将SR[m+1..t]归并排序,结果放入TR2[m+1..t]
Merge(TR2,TR1,s,m,t) //将有序的TR2[s..m] 和TR2[m+1..t]归并到TR1[s..t]
}
}
(3)归并排序分析
a.时间复杂度:O(nlogn)
b.空间复杂度:O(n)
c.归并排序是稳定的
并排序,结果放入TR2[m+1…t]
Merge(TR2,TR1,s,m,t) //将有序的TR2[s…m] 和TR2[m+1…t]归并到TR1[s…t]
}
}
**(3)归并排序分析**
a.时间复杂度:O(nlogn)
b.空间复杂度:O(n)
c.归并排序是稳定的