內部排序->基數排序->鏈式基數排序


文字描述

  基數排序是和前面各類排序方法完全不相同,前面幾篇文章介紹的排序算法的實現主要是通過關鍵字間的比較和移動記錄這兩種操作,而實現基數排序不需要進行記錄關鍵字間的比較。基數排序是一種借助多關鍵字排序的思想對單邏輯關鍵字進行排序的方法。先介紹下什么是多關鍵字排序,以引入鏈式基數排序算法。

 

  先介紹什么是多關鍵字排序:

  比如,對撲克牌進行排序,每張撲克牌有兩個“關鍵字”:花色(梅花<方塊<紅桃<黑桃)和面值(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 }
鏈式基數排序

 

運行

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM