【筆記】基數排序

Time-space發表於2017-11-17

  基數排序是一種藉助多關鍵字排序的思想對單邏輯關鍵字進行排序的方法。

  一般情況下,假設有n個記錄的序列

R1,R2,,Rn
{R_1,R_2,…,R_n}
且每個記錄Ri
R_i
中含有d個關鍵字(K0i,K1i,,Kd1i)
(K^0_i,K^1_i,…,K^{d-1}_i)
,則稱序列對關鍵字有序是指:對於序列中的任意兩個記錄Ri
R_i
Rj(1i<jn)
R_j(1\leq i<j\leq n)
都滿足下列有序關係:
(K0i,K1i,,Kd1i)<(K0j,K1j,,Kd1j)
(K^0_i,K^1_i,…,K^{d-1}_i)<(K^0_j,K^1_j,…,K^{d-1}_j)
其中K0
K^0
稱為最主位關鍵字,Kd1
K^{d-1}
稱為最次位關鍵字。

  最高位優先法(MSD法):先對最主位關鍵字K0

K^0
進行排序,按K1
K^1
值不同再分成若干更小的子序列,依次重複,直至對Kd2
K^{d-2}
進行排序後得到的每一子序列中的記錄都具有相同的關鍵字(K0i,K1i,,Kd2i)
(K^0_i,K^1_i,…,K^{d-2}_i)
,而後分別每個子序列對Kd1
K^{d-1}
進行排序,最後將所有子序列一次聯接在一起稱為一個有序序列。
  最低位優先法(LSD法):從最次位關鍵字Kd1
K^{d-1}
起進行排序。然後再對高一位的關鍵字Kd2
K^{d-2}
進行排序,一次重複,直至對K0
K^0
進行排序後便成為一個有序序列。

  MSD和LSD只約定按什麼樣的“關鍵字次序”來進行排序,而未規定對每個關鍵字進行排序時所用的方法。兩種排序方法的不同特點:若按MSD進行排序,必須將序列逐層分割成若干子序列,然後對各子序列參加排序;而按LSD進行排序時,不必分成子序列,對每個關鍵字都是整個序列參加排序,但對Ki(0id2)

K_i(0\leq i \leq d-2)
進行排序時,只能用穩定的排序方法。另一方面, 按LSD進行排序時,在一定的條件下(即對前一個關鍵字Ki(0id2)
K_i(0\leq i \leq d-2)
的不同值,後一個關鍵字Ki+1
K^{i+1}
均取相同值),也可以通過若干次“分配”和“收集”來實現排序。通常將對不同元素的分類稱為分配,排序的過程稱為收集。

  基本演算法思想:假設第i個元素ai

a_i
的關鍵字keyi
key_i
keyi
key_i
是由d位十進位制組成,即keyi=kdikd1ik1i
key_i=k^d_ik^{d-1}_i…k^1_i
,其中k1i
k^1_i
為最低位,kdi
k^d_i
為最高位,關鍵字的每一位數字都可作為一個關鍵字。首先將元素序列按照最低的關鍵字進行排序,然後從低位到高位直到最高位依次進行排序,這樣就完成了排序的過程。

  例如對於一組元素的關鍵字序列為(236,128,34,567,321,793,317,106),這組關鍵字位數最多的是3位,在排序之前,首先將所有元素都轉換為3位數字組成的數,即(236,128,034,567,321,793,317,106),對這組元素進行基數排序需要進行3趟分配和收集,首先需要對元素序列的關鍵字的最低位即個位上的數字進行分配和收集,然後對十位上的數字進行分配和收集,最後是對最高位的數字進行分配和收集。一般情況下, 採用連結串列實現基數排序。


這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

  由以上很容易看出,經過第1趟排序即對個位數字作為關鍵字進行分配後,關鍵字被分為10類,個位數字相同的數字被劃分為一類,對分配後的元素進行收集之後,得到以個位數字非遞減排列的元素。同理,經過第2趟分配和收集後,得到以十位數字非遞減排列的元素。經過第3趟分配和收集後,得到最終的排序結果。

  • 型別定義標頭檔案
 #define MAX_NUM_OF_KEY 8 /* 關鍵字項數的最大值 */
 #define RADIX 10 /* 關鍵字基數,此時是十進位制整數的基數 */
 #define MAX_SPACE 1000
 typedef int InfoType; /* 定義其它資料項的型別 */
 typedef int KeyType; /* 定義RedType型別的關鍵字為整型 */
 typedef struct
 {
   KeyType key; /* 關鍵字項 */
   InfoType otherinfo; /* 其它資料項 */
 }RedType; /* 記錄型別*/
 typedef char KeysType; /* 定義關鍵字型別為字元型 */
 typedef struct
 {
   KeysType keys[MAX_NUM_OF_KEY]; /* 關鍵字 */
   InfoType otheritems; /* 其它資料項 */
   int next;
 }SLCell; /* 靜態連結串列的結點型別 */

 typedef struct
 {
   SLCell r[MAX_SPACE]; /* 靜態連結串列的可利用空間,r[0]為頭結點 */
   int keynum; /* 記錄的當前關鍵字個數 */
   int recnum; /*  靜態連結串列的當前長度 */
 }SLList; /* 靜態連結串列型別 */

 typedef int ArrType[RADIX]; /* 指標陣列型別 */
  • 函式檔案
void InitList(SLList *L,RedType D[],int n)
 { /* 初始化靜態連結串列L(把陣列D中的資料存於L中) */
   char c[MAX_NUM_OF_KEY],c1[MAX_NUM_OF_KEY];
   int i,j,max=D[0].key; /* max為關鍵字的最大值 */
   for(i=1;i<n;i++)
     if(max<D[i].key)
       max=D[i].key;
   (*L).keynum=(int)(ceil(log10(max)));
   (*L).recnum=n;
   for(i=1;i<=n;i++)
   {
     (*L).r[i].otheritems=D[i-1].otherinfo;
     itoa(D[i-1].key,c,10); /* 將10進位制整型轉化為字元型,存入c */
     for(j=strlen(c);j<(*L).keynum;j++) /* 若c的長度<max的位數,在c前補'0' */
     {
       strcpy(c1,"0");
       strcat(c1,c);
       strcpy(c,c1);
     }
     for(j=0;j<(*L).keynum;j++)
       (*L).r[i].keys[j]=c[(*L).keynum-1-j];
   }
 }

 int ord(char c)
 { /* 返回k的對映(個位整數) */
   return c-'0';
 }

 void Distribute(SLCell r[],int i,ArrType f,ArrType e)
 { /* 靜態鍵表L的r域中記錄已按(keys[0],...,keys[i-1])有序。*/
   /* 按第i個關鍵字keys[i]建立RADIX個子表,使同一子表中記錄的keys[i]相同。 */
   /* f[0..RADIX-1]和e[0..RADIX-1]分別指向各子表中第一個和最後一個記錄 */
   int j,p;
   for(j=0;j<RADIX;++j)
     f[j]=0; /* 各子表初始化為空表 */
   for(p=r[0].next;p;p=r[p].next)
   {
     j=ord(r[p].keys[i]); /* ord將記錄中第i個關鍵字對映到[0..RADIX-1] */
     if(!f[j])
       f[j]=p;
     else
       r[e[j]].next=p;
     e[j]=p; /* 將p所指的結點插入第j個子表中 */
   }
 }

 int succ(int i)
 { /* 求後繼函式 */
   return ++i;
 }

 void Collect(SLCell r[],ArrType f,ArrType e)
 { /* 按keys[i]自小至大地將f[0..RADIX-1]所指各子表依次連結成 */
   /* 一個連結串列,e[0..RADIX-1]為各子表的尾指標。 */
   int j,t;
   for(j=0;!f[j];j=succ(j)); /* 找第一個非空子表,succ為求後繼函式 */
   r[0].next=f[j];
   t=e[j]; /* r[0].next指向第一個非空子表中第一個結點 */
   while(j<RADIX-1)
   {
     for(j=succ(j);j<RADIX-1&&!f[j];j=succ(j)); /* 找下一個非空子表 */
     if(f[j])
     { /* 連結兩個非空子表 */
       r[t].next=f[j];
       t=e[j];
     }
   }
   r[t].next=0; /* t指向最後一個非空子表中的最後一個結點 */
 }

 void RadixSort(SLList *L)
 { /* L是採用靜態連結串列表示的順序表。對L作基數排序,使得L成為按關鍵字 */
   /* 自小到大的有序靜態連結串列,L.r[0]為頭結點。 */
   int i;
   ArrType f,e;
   for(i=0;i<(*L).recnum;++i)
     (*L).r[i].next=i+1;
   (*L).r[(*L).recnum].next=0; /* 將L改造為靜態連結串列 */
   for(i=0;i<(*L).keynum;++i)
   { /* 按最低位優先依次對各關鍵字進行分配和收集 */
     Distribute((*L).r,i,f,e); /* 第i趟分配 */
     Collect((*L).r,f,e); /* 第i趟收集 */
     printf("第%d趟收集後:\n",i+1);
     printl(*L);
     printf("\n");
   }
 }

 void Sort(SLList L,int adr[]) /* 改此句(型別) */
 { /* 求得adr[1..L.length],adr[i]為靜態連結串列L的第i個最小記錄的序號 */
   int i=1,p=L.r[0].next;
   while(p)
   {
     adr[i++]=p;
     p=L.r[p].next;
   }
 }

 void Rearrange(SLList *L,int adr[]) /* 改此句(型別) */
 { /* adr給出靜態連結串列L的有序次序,即L.r[adr[i]]是第i小的記錄。 */
   /* 本演算法按adr重排L.r,使其有序。演算法10.18(L的型別有變) */
   int i,j,k;
   for(i=1;i<(*L).recnum;++i) /* 改此句(型別) */
     if(adr[i]!=i)
     {
       j=i;
       (*L).r[0]=(*L).r[i]; /* 暫存記錄(*L).r[i] */
       while(adr[j]!=i)
       { /* 調整(*L).r[adr[j]]的記錄到位直到adr[j]=i為止 */
         k=adr[j];
         (*L).r[j]=(*L).r[k];
         adr[j]=j;
         j=k; /* 記錄按序到位 */
       }
       (*L).r[j]=(*L).r[0];
       adr[j]=j;
     }
 }
  • 主程式
 #define N 10
 void print(SLList L)
 { /* 按陣列序號輸出靜態連結串列 */
   int i,j;
   printf("keynum=%d recnum=%d\n",L.keynum,L.recnum);
   for(i=1;i<=L.recnum;i++)
   {
     printf("keys=");
     for(j=L.keynum-1;j>=0;j--)
       printf("%c",L.r[i].keys[j]);
     printf(" otheritems=%d next=%d\n",L.r[i].otheritems,L.r[i].next);
   }
 }

 void printl(SLList L)
 { /* 按連結串列輸出靜態連結串列 */
   int i=L.r[0].next,j;
   while(i)
   {
     for(j=L.keynum-1;j>=0;j--)
       printf("%c",L.r[i].keys[j]);
     printf(" ");
     i=L.r[i].next;
   }
 }

 void main()
 {
   RedType d[N]={{278,1},{109,2},{63,3},{930,4},{589,5},{184,6},{505,7},{269,8},{8,9},{83,10}};
   SLList l;
   int *adr;
   InitList(&l,d,N);
   printf("排序前(next域還沒賦值):\n");
   print(l);
   RadixSort(&l);
   printf("排序後(靜態連結串列):\n");
   print(l);
   adr=(int*)malloc((l.recnum)*sizeof(int));
   Sort(l,adr);
   Rearrange(&l,adr);
   printf("排序後(重排記錄):\n");
   print(l);
 }
  • 測試結果


這裡寫圖片描述

相關文章