文字描述
基數排序是和前面各類排序方法完全不相同,前面幾篇文章介紹的排序算法的實現主要是通過關鍵字間的比較和移動記錄這兩種操作,而實現基數排序不需要進行記錄關鍵字間的比較。基數排序是一種借助多關鍵字排序的思想對單邏輯關鍵字進行排序的方法。先介紹下什么是多關鍵字排序,以引入鏈式基數排序算法。
先介紹什么是多關鍵字排序:
比如,對撲克牌進行排序,每張撲克牌有兩個“關鍵字”:花色(梅花<方塊<紅桃<黑桃)和面值(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 }
運行

