• 内部排序->基数排序->链式基数排序


    文字描述

      基数排序是和前面各类排序方法完全不相同,前面几篇文章介绍的排序算法的实现主要是通过关键字间的比较和移动记录这两种操作,而实现基数排序不需要进行记录关键字间的比较。基数排序是一种借助多关键字排序的思想对单逻辑关键字进行排序的方法。先介绍下什么是多关键字排序,以引入链式基数排序算法。

      先介绍什么是多关键字排序:

      比如,对扑克牌进行排序,每张扑克牌有两个“关键字”:花色(梅花<方块<红桃<黑桃)和面值(2<3<,…,A),且“花色”的地位高于”面值”, 那么对扑克牌排序有两种方法:

      方法1:先按不同“花色”分成有次序的4堆,每一堆的”花色”相同; 然后分别对每一堆内部按”面值”大小整理有序。这种先对主键字字进行排序,再对次关键字排序的方法叫最高位优先法(简称MSD: Most Significant first)

      方法2:先按不同”面值”分成13堆,然后将13堆自小到大叠在一起(“3”在”2”之上,”4”在”3”之上,…,最上面的是4张”A”),然后将这幅牌整个颠倒过来再重新按不同花色分成4堆,最后将这4堆按自小到的次序合在一起(梅花在最下面,黑桃在最上面)。这种先对次键字字进行排序,再对主关键字排序的方法叫最低位优先法(简称LSD: Least Significant first)

      采用第二种方法LSD法对多关键字进行排序时,也可以不采用之前介绍的各种通过关键字间的比较来实现排序的方法,而是通过若干次“分配”和“收集”来实现排序。

      关于链式基数排序的介绍:

      采用多关键字排序中的LSD方法,先对低优先级关键字排序,再按照高点的优先级关键字排序,不过基数排序在排序过程中不需要经过关键字的比较,而是借助“分配”和“收集”两种操作对单逻辑关键字进行排序的一种内部排序方法。

      比如,若关键字是十进制表示的数字,且范围在[0,999]内,则可以把每一个十进制数字看成由三个关键字组成(K0, K1, K2),其中K0是百位数,K1是十位数,K2是个位数。基RADIX的取值为10; 按LSD进行排序,从最低位关键字起,按关键字的不同值将序列中记录“分配”到RADIX个队列中后再“收集”之,如此重复d次。按这种方法实现的排序称之为基数排序,以链表作存储结构的基数排序叫链式基数排序。

    示意图

    算法分析

      对n个记录(假设每个记录含d个关键字,每个关键字的取值范围为rd个值)进行链式基数排序的时间复杂度为d*(n+rd),其中每一躺分配的时间复杂度为n,每一躺收集的时间复杂度为rd,整个排序需进行d躺分配和收集。

      所需辅助空间为2*rd个队列指针,由于采用链表作存储结构,相对于其他采用顺序存储结构的排序方法而言,还增加了n个指针域的空间。

      链式基数排序是稳定的排序。

    代码实现

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 
      5 #define DEBUG
      6 
      7 #define EQ(a, b) ((a) == (b))
      8 #define LT(a, b) ((a) <  (b))
      9 #define LQ(a, b) ((a) <= (b))
     10 
     11 //关键字项数的最大个数
     12 #define MAX_NUM_OF_KEY    8
     13 //关键字基数,此时是十进制整数的基数就是10
     14 #define RADIX            10
     15 //静态链表的最大长度
     16 #define MAX_SPACE        10000
     17 
     18 //定义结点中的关键字类型为int
     19 typedef int KeyType;
     20 //定义结点中除关键字外的附件信息为char
     21 typedef char InfoType;
     22 
     23 //静态链表的结点类型
     24 typedef struct{
     25     //关键字
     26     KeyType    keys[MAX_NUM_OF_KEY];
     27     //除关键字外的其他数据项
     28     InfoType otheritems;
     29     int next;
     30 }SLCell;
     31 
     32 //静态链表类型
     33 typedef struct{
     34     //静态链表的可利用空间,r[0]为头结点
     35     SLCell r[MAX_SPACE];
     36     //每个记录的关键字个数
     37     int keynum;
     38     //静态链表的当前长度
     39     int recnum;
     40 }SLList;
     41 
     42 //指针数组类型
     43 typedef int ArrType[RADIX];
     44 
     45 void PrintSList(SLList L)
     46 {
     47     int i = 0;
     48     printf("下标值 ");
     49     for(i=0; i<=L.recnum; i++){
     50         printf(" %-6d", i);
     51     }
     52     printf("
    关键字 ");
     53     for(i=0; i<=L.recnum; i++){
     54         printf(" %-1d%-1d%-1d,%-2c", L.r[i].keys[2], L.r[i].keys[1], L.r[i].keys[0], L.r[i].otheritems);
     55     }
     56 //    printf("
    其他值 ");
     57 //    for(i=0; i<=L.recnum; i++){
     58 //        printf(" %-5c", L.r[i].otheritems);
     59 //    }
     60     printf("
    下一项 ");
     61     for(i=0; i<=L.recnum; i++){
     62         printf(" %-6d", L.r[i].next);
     63     }
     64     printf("
    ");
     65     return;
     66 }
     67 
     68 void PrintArr(ArrType arr, int size)
     69 {
     70     int i = 0;
     71     for(i=0; i<size; i++){
     72         printf("[%d]%-2d ", i, arr[i]);
     73     }
     74     printf("
    ");
     75 }
     76 
     77 /*
     78  *静态链表L的r域中记录已按(key[0],...,key[i-1])有序
     79  *本算法按第i个关键字keys[i]建立RADIX个子表,使同一子表中记录的keys[i]相同。
     80  *f[0,...,RADIX-1]和e[0,...,RADIX-1]分别指向各子表中的第一个记录和最后一个记录。
     81  */
     82 void Distribute(SLCell *r, int i, ArrType f, ArrType e)
     83 {
     84     int j = 0;
     85     //各子表初始化为空
     86     for(j=0; j<RADIX; j++)
     87         f[j] = e[j] = 0;
     88 
     89     int p = 0;
     90     for(p=r[0].next; p; p=r[p].next){
     91         j = r[p].keys[i];
     92         if(!f[j])
     93             f[j] = p;
     94         else
     95             r[e[j]].next = p;
     96         //将p所指的结点插入第j个字表中
     97         e[j] = p;
     98     }
     99 }
    100 
    101 /*
    102  * 本算法按keys[i]自小到大地将f[0,...,RADIX-1]所指各子表依次链接成一个链表
    103  * e[0,...,RADIX-1]为各子表的尾指针
    104  */
    105 void Collect(SLCell *r, int i, ArrType f, ArrType e){
    106     int j = 0, t = 0;
    107     //找到第一个非空子表,
    108     for(j=0; !f[j]; j++);
    109     //r[0].next指向第一个非空子表的第一个结点
    110     r[0].next = f[j];
    111     //t指向第一个非空子表的最后结点
    112     t = e[j];
    113     while(j<RADIX){
    114         //找下一个非空子表
    115         for(j+=1; !f[j]; j++);
    116         //链接两个非空子表
    117         if(j<RADIX && f[j]){
    118             r[t].next = f[j];
    119             t = e[j];
    120         }
    121     }
    122     //t指向最后一个非空子表中的最后一个结点
    123     r[t].next = 0;
    124 }
    125 
    126 /*
    127  * L是采用静态链表表示的顺序表。
    128  * 对L作基数排序,使得L成为按关键字自小到大的有效静态链表,L->r[0]为头结点
    129  */
    130 void RadixSort(SLList *L)
    131 {
    132     int i = 0;
    133     //将L改造成静态链表
    134     for(i=0; i<L->recnum; i++)
    135         L->r[i].next = i+1;
    136     L->r[L->recnum].next = 0;
    137 #ifdef DEBUG
    138     printf("将L改造成静态链表
    ");
    139     PrintSList(*L);
    140 #endif
    141 
    142     ArrType f, e;
    143     //按最低位优先依次对各关键字进行分配和收集
    144     for(i=0; i<L->keynum; i++){
    145         //第i趟分配
    146         Distribute(L->r, i, f, e);
    147 #ifdef DEBUG
    148         printf("第%d趟分配---------------------------------------
    ");
    149         PrintSList(*L);
    150         printf("头指针队列:");
    151         PrintArr(f, RADIX);
    152         printf("尾指针队列:");
    153         PrintArr(e, RADIX);
    154 #endif
    155         //第i躺收集
    156         Collect(L->r, i, f, e);
    157 #ifdef DEBUG
    158         printf("第%d趟收集----
    ");
    159         PrintSList(*L);
    160         printf("按next打印:");
    161         int p = 0;
    162         for(p=L->r[0].next; p; p=L->r[p].next){
    163             printf("%d%d%d ", L->r[p].keys[2], L->r[p].keys[1], L->r[p].keys[0]);
    164         }
    165         printf("
    ");
    166 #endif
    167     }
    168 }
    169 
    170 int getRedFromStr(char str[], int i, SLCell *result)
    171 {
    172     int key = atoi(str);
    173     if(key<0 || key >999){
    174         printf("Error:too big!
    ");
    175         return -1;
    176     }
    177     int units = 0, tens = 0, huns = 0;
    178     //百位
    179     huns = key/100;
    180     //十位
    181     tens = (key-100*huns)/10;
    182     //个位
    183     units = (key-100*huns-10*tens)/1;
    184     result->keys[0] = units;
    185     result->keys[1] = tens;
    186     result->keys[2] = huns;
    187     result->otheritems = 'a'+i-1;
    188     return 0;
    189 }
    190 
    191 
    192 int main(int argc, char *argv[])
    193 {
    194     SLList L;
    195     int i = 0;
    196     for(i=1; i<argc; i++){
    197         if(i>MAX_SPACE)
    198             break;
    199         if(getRedFromStr(argv[i], i, &L.r[i]) < 0){
    200             printf("Error:only 0-999!
    ");
    201             return -1;
    202         }
    203     }
    204     L.keynum = 3;
    205     L.recnum = i-1;
    206     L.r[0].next = 0;
    207     L.r[0].otheritems = '0';
    208     RadixSort(&L);
    209     return 0;
    210 }
    链式基数排序

    运行

  • 相关阅读:
    Gradle在大型Java项目上的应用
    2015年,移动开发都有哪些热点?
    为什么寄存器比内存快?
    Gogs:可能是比Gitlab更好的选择
    自定义元素–为你的HTML代码定义新元素
    在DLL编程中,导出函数为什么需要extern "C"
    c调用c++编的dll,c++调用c编写的dll,extern “C”的用法
    C/C++:函数的编译方式与调用约定以及extern “C”的使用
    在VS2015中用C++编写可被其它语言调用的动态库DLL
    C++在VS下创建、调用dll
  • 原文地址:https://www.cnblogs.com/aimmiao/p/9397464.html
Copyright © 2020-2023  润新知