排序(2)--选择排序,归并排序和基数排序
来源:互联网 发布:win7 64位旗舰优化版 编辑:程序博客网 时间:2024/05/03 10:26
一.选择排序:
1.简单选择排序:
(1).思想: 首先通过 n –1 次关键字比较,从 n 个记录中找出关键字最小的记录,将它与第一个记录交换。
再通过 n –2 次比较,从剩余的 n –1 个记录中找出关键字次小的记录,将它与第二个记录交换。
重复上述操作,共进行 n –1 趟排序后,排序结束。
(2)实现:
void SelectSort (SqList &L) { // 对顺序表 L 作简单选择排序 for (i = 1; i < L.length; ++ i) { k = i; for ( j = i+1; j <= n; j++) if (L.r[j].key < L.r[k].key) k = j; if (i != k) L.r[i]←→L.r[k]; // 与第 i 个记录交换 } } // SelectSort(3).算法分析:
时间复杂度:O(n2);
空间复杂度:O(1)——交换时用到一个暂存单元
稳定性:由于存在着不相邻元素之间的互换,因此,简单选择排序是“不稳定的” 。
2.树形选择排序(竞标赛排序):
(1)思想: 首先对 n 个记录的关键字进行两两比较,得到 n/2个优胜者(关键字小者),作为第一步比较的结果保留下来。然后在这 n/2 个较小者之间再进行两两比 较,…,如此重复,直到选出最小关键字的记录为止
.......................
(2)算法分析:
时间复杂度:O(nlog2n)-----------n个记录各自比较约log2n次
空间复杂度:O(n) —胜者树的附加内结点共有n-1个!
稳定性:稳定------左右结点相同者左为先
3.堆排序:
(1)概念:设有n个元素的序列k1,k2,…,kn,当且仅当满足下述关系之一时,称之为堆
*树中所有结点的值均大于(或小于)其左右孩子,此树的根结点(即堆顶)必最大(或最小)
(2)建堆(建小堆为例):
假若完全二叉树的某一个结点i,它的左、右子树已是堆。需要将R[2i].key与R[2i+1].key之中的最小者与R[i].key比较,若R[i].key较大则交换,这有可能破坏下 一级堆。于是继续采用上述方法构造下一级堆,直到完全二叉树中结点i构成堆为止。
void HeapAdjust(SqList &H, int s, int m) {//已知H.r[s..m]中记录的关键字除H.r[s].key之外均满足堆的定义, //本函数调整H.r[s]的关键字,使H.r[s..m]成为一个大顶堆 rc = H.r[s]; for (j=2*s; j<=m; j*=2) { //沿key较大的孩子结点向下筛选,j为key较大的记录的下标 if(j<m && LT(H.r[j].key, H.r[j+1].key)) ++j; if(!LT(rc.key,H.r[j].key)) break; H.r[s]=H.r[j]; s=j; //rc应插入在位置s上 } H.r[s] = rc; //插入 } //HeapAdjust(3)怎样进行堆排序
关键:将堆的当前顶点输出后,如何将剩余序列重新调整为堆?
方法:将当前顶点与堆尾记录交换,然后仿建堆动作重新调整,如此反复直至排序结束。
对建好的大堆进行排序:
.....知道最后记录是从小到大的。
void HeapSort (HeapType &H) { //对顺序表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..i]中最后一个记录相互交换 H.r[1]<---> H.r[i]; HeapAdjust(H,1,i-1); //将H.R[1..i-1]重新调整为大顶堆 }} //HeapSort(4)算法分析;
时间效率: O(nlog2n)。因为整个排序过程中需要调用n-1次HeapAdjust( )算法, HeapAdjust( )而算法本身耗时为log2n;
空间效率:O(1)。仅在第二个for循环中交换记录时用到一个临时变量temp。
稳定性: 不稳定。
优点:对少量数据效果不明显,但对大量数据有效
二.归并排序:
1.思想:将两个(或以上)的有序表组成新的有序表。
可以把一个长度为n的无序序列看成是 n 个长度为 1 的有序子序列 ,首先做两两归并,得到 n / 2 个长度为 2 的子序列 ;再做两两归并,…,如此重复,直到最后 得到一个长度为n 的有序序列。
2.实现:
一趟归并排序算法 (两路有序并为一路)
void Merge (SR,&TR,i, m, n) { // 将有序的SR[i…m]和SR[m+1…n]归并为有序的TR[i…n] for(k=i , j=m+1; i<=m && j<=n; ++k ) { if ( SR[i]<= SR[j] )TR[k]=SR[i++]; else TR[k]=SR[j++]; // 将SR中记录由小到大地并入TR } if (i<=m) TR[k…n]=SR[i…m]; // 将剩余的SR[i…m]复制到TR if (j<=n) TR[k…n]=SR[j…n]; // 将剩余的SR[j…n]复制到TR} // Merge递归形式的两路归并排序算法
void MSort (SR,&TR1,s, t) { // 将无序的SR[s…t]归并排序为TR1[s…t] if ( s==t )TR1[s]=SR[s]; // 当len=1时返回 else { m=(s+t)/2; // 将SR [s…t]平分为SR [s…m]和SR [m+1…t] MSort (SR,&TR2,s, m); // 将SR 一分为二, 2分为4… // 递归地将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] } } // MSort*初次调用时为(L, TR, 1, length)
3.算法分析:
时间效率 :O(nlog2n) 一趟归并排序的操作是:调用[n/2h]次算法merge将SR[1..n]中前后相邻且长度为h的有序段进行两两归并,得到前后相邻长度为2h的有序段, 并存放在TR[1..n]中,整个归并排序需要进行[log2n]趟,所以算法总的时间复杂度为O(nlog2n)。
空间效率 :O(n) 因为需要一个与原始序列同样大小的辅助序列(TR)。这正是此算法的缺点。
稳定性:稳定
三.基数排序:
1.两种思路:
最高位优先法MSD (Most Significant Digit first)
最低位优先法LSD (Least Significant Digit first)
例:对一副扑克牌该如何排序?
答:若规定花色为第一关键字(高位),面值为第二关键字(低位),则使用MSD和LSD方法都可以达到排序目的。
MSD方法的思路:先设立4个花色“箱”,将全部牌按花色分别归入4个箱内(每个箱中有13张牌);然后对每个箱中的牌按面值进行插入排序(或其它稳 定算法)。
LSD方法的思路:先按面值分成13堆(每堆4张牌),然后对每堆中的牌按花色进行排序(用插入排序等稳定的算法)。
2.计算机实现:
再看一例:
例:初始关键字序列T=(32, 13, 27, 32*, 19,33),请分别用MSD和LSD进行排序,并讨论其优缺点。
法1(MSD):原始序列:32, 13, 27, 32*, 19, 33
先按高位Ki1 排序:(13, 19), 27, (32, 32*,33)
再按低位 Ki2排序 : 13, 19 , 27, 32, 32*, 33
因为有分组,故此算法需递归实现
法2(LSD): 原始序列: 32, 13, 27, 32*, 19 ,33
先按低位Ki2排序: 32, 32*, 13, 33, 27, 19
再按高位Ki1排序: 13, 19 , 27, 32, 32*, 33
无需分组,易编程实现!
3.LSD实现(顺序表);
排序时经过了反复的“分配”和“收集”过程。当对关键字所有的位进行扫描排序后,整个序列便从无序变为有序了。
讨论:所用队列是顺序结构,浪费空间,能否改用链式结构?
4.LSD实现(链式)
5.算法分析;
假设有n 个记录, 每个记录的关键字有d 位,每个关键字的取值有radix个, 则需要radix个队列, 进行d趟“分配”与“收集”。因此时间复杂度:O( d ( n+radix ) )。
空间复杂度:O(radix). 基数排序需要增加n+2radix个附加链接指针,空间效率低
稳定性:稳定。(一直前后有序)。
- 排序(2)--选择排序,归并排序和基数排序
- 归并排序和基数排序
- 插入排序、交换排序、选择排序、归并排序、基数排序
- 插入排序/选择排序/交换排序/归并排序/基数排序
- 归并排序,堆排序,基数排序,希尔排序,快速排序,交换排序,选择排序和插入排序的总结和比较
- 插入排序、交换排序、选择排序、归并排序和基数排序算法的比较
- C# 插入排序 冒泡排序 选择排序 快速排序 堆排序 归并排序 基数排序 希尔排序
- 选择排序和归并排序
- 选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法, 冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。
- 选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。
- 快速排序 选择排序 插入排序 冒泡排序 堆排序 归并排序 基数排序
- 希尔排序,基数排序,归并排序
- 【数据结构】-归并排序,基数排序
- 排序算法(2)冒泡排序,快速排序,归并排序和基数排序MSD,LSD
- 选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序
- 【Java】八个常用的排序算法:插入排序、冒泡排序、选择排序、希尔排序 、快速排序、归并排序、堆排序和LST基数排序
- 算法-排序-归并排序和基数排序(排序总结)
- 排序算法系列-堆排序-快速排序-基数排序-简单选择排序-归并排序
- 杭电第一题
- CSS架构:代码简洁原则
- [bzoj][SCOI2007]降雨量
- 如何设计好一款iOS游戏UI的细节
- 求子数组的最大和
- 排序(2)--选择排序,归并排序和基数排序
- Oracle导出命令简介
- scrum实践及心得体会1
- 怎么写论文0
- 【ZATH学摄影Vol.001】RGB与Bayer滤镜
- hdu 2276 Kiki & Little Kiki 2(矩阵乘法)
- boost::algorithm(字符串算法库)
- Linux 视频设备驱动V4L2最常用的控制命令使用说明(1.02)
- OS X 10.9.1下编译GCC 4.8.2