内部排序->基数排序->链式基数排序
文字描述
基数排序是和前面各类排序方法完全不相同,前面几篇文章介绍的排序算法的实现主要是通过关键字间的比较和移动记录这两种操作,而实现基数排序不需要进行记录关键字间的比较。基数排序是一种借助多关键字排序的思想对单逻辑关键字进行排序的方法。先介绍下什么是多关键字排序,以引入链式基数排序算法。
先介绍什么是多关键字排序:
比如,对扑克牌进行排序,每张扑克牌有两个“关键字”:花色(梅花<方块<红桃<黑桃)和面值(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("\n关键字 "); 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("\n其他值 "); 57 // for(i=0; i<=L.recnum; i++){ 58 // printf(" %-5c", L.r[i].otheritems); 59 // } 60 printf("\n下一项 "); 61 for(i=0; i<=L.recnum; i++){ 62 printf(" %-6d", L.r[i].next); 63 } 64 printf("\n"); 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("\n"); 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改造成静态链表\n"); 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趟分配---------------------------------------\n"); 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趟收集----\n"); 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("\n"); 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!\n"); 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!\n"); 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 }
运行
posted on 2018-07-31 18:13 LiveWithACat 阅读(4052) 评论(0) 编辑 收藏 举报