排序(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个附加链接指针,空间效率低
                     稳定性:稳定。(一直前后有序)。

0 0
原创粉丝点击