資料結構與演算法——基數排序簡單Java實現

Mr.Johness發表於2013-11-22

  基數排序(radix sort)又稱“桶子法”,在對多個正整數進行排序時可以使用。它的靈感來自於佇列(Queue),它最獨特的地方在於利用了數字的有窮性(阿拉伯數字只有0到9的10個)。

  基數排序使用11個動態陣列實現排序演算法,一個主佇列(下文都將使用的動態陣列稱為佇列)儲存未排序的資料(最後排序完成也仍然可以用它儲存,或者如果希望儲存原來的資料位置則可能需要增加一個佇列);10個子佇列用於排序過程中動態存放數值,在排序開始前和結束後可以清空。

  我們使用LinkedList類來實現基數排序,其實整個類很簡單,我先貼出全部程式碼然後再細細解釋:

  1 package ahe.sort;
  2 
  3 import java.io.BufferedReader;
  4 import java.io.IOException;
  5 import java.io.InputStreamReader;
  6 import java.util.LinkedList;
  7 
  8 /**
  9  * 基數排序演算法
 10  * 
 11  * @author Johness
 12  *
 13  */
 14 public class RadixSort {
 15 
 16     /** 主佇列 */
 17     private LinkedList<Integer> mainQueue;
 18     /** 子佇列 */
 19     private LinkedList<Integer>[] subQueues;
 20     /** 子佇列個數,作用不大 */
 21     private final int SIZE = 10;
 22     /** 當前容器(主佇列)中儲存數值的最大位數 */
 23     private int maxDigits;
 24     
 25     /** 建構函式 */
 26     public RadixSort() {
 27         mainQueue = new LinkedList<Integer>();
 28         subQueues = new LinkedList[SIZE];
 29         for(int i = 0; i < SIZE; ++i)
 30             subQueues[i] = new LinkedList<Integer>();
 31         maxDigits = 0;
 32     }
 33     
 34     /** 向容器中(主佇列)新增一個數值 */
 35     public void add(Integer num) {
 36         int digits = String.valueOf(num).length();
 37         if (digits > maxDigits)
 38             maxDigits = digits;
 39         mainQueue.add(num);
 40     }
 41     
 42     /** 排序 */
 43     public void sort() {
 44         for (int i = 1; i <= maxDigits; ++i) {
 45             while (mainQueue.size() > 0) {
 46                 Integer element = (Integer) mainQueue.pop();
 47                 String elementTmpStr = String.valueOf(element);
 48                 if (elementTmpStr.length() < i) {
 49                     subQueues[0].add(element);
 50                     continue;
 51                 }
 52                 int digit = elementTmpStr.charAt(elementTmpStr.length() - i) - '0';
 53                 subQueues[digit].add(element);
 54             }
 55             //listSubQueues();
 56             for (int j = 0; j < SIZE; ++j) {
 57                 mainQueue.addAll(subQueues[j]);
 58                 subQueues[j].clear();
 59             }
 60             //listMainQueue();
 61         }
 62     }
 63     
 64     /*==============================================================================================*/
 65     // 以下方法為測試方法(以下方法來自於Arizona State University學校課後作業,本人只做翻譯,如該資源侵犯了您的權益,請及時聯絡我)
 66     // 您可以訪問
 67     // https://courses.eas.asu.edu/cse205/current/assignments/assignment11/Assignment11.java
 68     // https://courses.eas.asu.edu/cse205/current/assignments/assignment11/Sorting.java
 69     // 檢視本文參考內容
 70     // 本文輸入輸出對照表可從該課後作業中獲得
 71     // 輸入表
 72     // https://courses.eas.asu.edu/cse205/current/assignments/assignment11/input1.txt
 73     // https://courses.eas.asu.edu/cse205/current/assignments/assignment11/input2.txt
 74     // https://courses.eas.asu.edu/cse205/current/assignments/assignment11/input3.txt
 75     // https://courses.eas.asu.edu/cse205/current/assignments/assignment11/input4.txt
 76     // 輸出表
 77     // https://courses.eas.asu.edu/cse205/current/assignments/assignment11/output1.txt
 78     // https://courses.eas.asu.edu/cse205/current/assignments/assignment11/output2.txt
 79     // https://courses.eas.asu.edu/cse205/current/assignments/assignment11/output3.txt
 80     // https://courses.eas.asu.edu/cse205/current/assignments/assignment11/output4.txt
 81     /*==============================================================================================*/
 82     /**
 83      * 列舉(輸出)主佇列資料
 84      */
 85     public void listMainQueue() {
 86         System.out.println("mainQueue = " + listQueue(mainQueue) + "\n");
 87     }
 88 
 89     /**
 90      * 列舉(輸出)子佇列資料
 91      */
 92     public void listSubQueues() {
 93         String result = "";
 94         for (int i = 0; i < SIZE; i++) {
 95             result += "subQueue[" + i + "]:";
 96             result += listQueue(subQueues[i]);
 97             result += "\n";
 98         }
 99         System.out.println(result);
100     }
101 
102     /**
103      * 列舉某佇列中資料項
104      * 
105      * 方法使用了一個臨時佇列來完成資料輪循
106      * 先從目標佇列中逐個取出(採用取出並刪除的方式)放入臨時佇列並做列舉操作(連線到返回字串)
107      * 待輪循完成後再將臨時佇列中的資料存入回目標佇列
108      * 
109      * @param queue 目標佇列
110      * @return 包含目標佇列中所有資料的字串
111      */
112     private String listQueue(LinkedList<Integer> queue) {
113         LinkedList<Integer> temp = new LinkedList<Integer>();
114         String result = "{ ";
115 
116         while (!queue.isEmpty()) {
117             Integer removed = queue.remove();
118             result += removed + " ";
119             temp.offer(removed);
120         }
121         result += "}\n";
122 
123         while (!temp.isEmpty()) {
124             Integer removed2 = temp.remove();
125             queue.offer(removed2);
126         }
127         return result;
128     }
129     
130     public static void main(String[] args) {
131         char input1;
132         String inputInfo = new String();
133         String line = new String();
134 
135         RadixSort sort1 = new RadixSort();
136 
137         try {
138             // 列印選單
139             printMenu();
140 
141             // 建立流讀取器讀取使用者輸入
142             InputStreamReader isr = new InputStreamReader(System.in);
143             BufferedReader stdin = new BufferedReader(isr);
144 
145             do {
146                 System.out.print("你想進行什麼操作?\n");
147                 line = stdin.readLine().trim(); // 讀取一行
148                 input1 = line.charAt(0);
149                 input1 = Character.toUpperCase(input1);
150 
151                 if (line.length() == 1) // 檢查輸入指令是否為單個
152                                         // 字元
153                 {
154                     switch (input1) {
155                     case 'A': // 新增一個數值
156                         System.out.print("請輸入要新增的數值:\n");
157                         inputInfo = stdin.readLine().trim();
158                         int num = Integer.parseInt(inputInfo);
159                         sort1.add(num);
160                         System.out.print("數值新增成功\n");
161                         break;
162                     case 'L': // 列舉數值
163                         sort1.listMainQueue();
164                         break;
165                     case 'Q': // 退出
166                         break;
167                     case 'S': // 排序
168                         sort1.sort();
169                         System.out.print("排序完成\n");
170                         break;
171                     case '?': // 顯示幫助
172                         printMenu();
173                         break;
174                     default:
175                         System.out.print("未知指令\n");
176                         break;
177                     }
178                 } else {
179                     System.out.print("未知指令\n");
180                 }
181             } while (input1 != 'Q' || line.length() != 1);
182         } catch (IOException exception) {
183             System.out.print("IO Exception\n");
184         }
185     }
186 
187     /** 列印控制檯介面(選單) */
188     public static void printMenu() {
189         System.out.print("選項\t\t動作\n" + "------\t\t------\n"
190                 + "A\t\t新增一個數值\n" + "L\t\t列舉佇列\n"
191                 + "Q\t\t退出\n" + "S\t\t排序資料\n"
192                 + "?\t\t顯示幫助\n\n");
193     }
194 }

  我們直接看sort方法(行43至62),在略去了每次排序資料從子佇列存回主佇列的程式碼(行56至59)後我們的排序演算法主要分為兩層。

  外層一共需要迴圈最大數值位數次,內層(每次)則是需要迴圈數值個數次。以{1,22,333,4444}為例,外層為4次迴圈(最大數4444為4位數),內層為4次迴圈(共有4個元素需要排序)。

  我們把排序過程中列舉佇列的程式碼取消註釋(行55和60),我們使用輸入表1進行輸入:

a
539
a
264
a
372
a
424
a
419
a
129
a
322
a
544
a
367
l
s
l
q

  對應輸出表則應該如下(部分空白我去除了):

 1 選項        動作
 2 ------        ------
 3 A        新增一個數值
 4 L        列舉佇列
 5 Q        退出
 6 S        排序資料
 7 ?        顯示幫助
 8 
 9 你想進行什麼操作?
10 a
11 請輸入要新增的數值:
12 539
13 數值新增成功
14 你想進行什麼操作?
15 a
16 請輸入要新增的數值:
17 264
18 數值新增成功
19 你想進行什麼操作?
20 a
21 請輸入要新增的數值:
22 372
23 數值新增成功
24 你想進行什麼操作?
25 a
26 請輸入要新增的數值:
27 424
28 數值新增成功
29 你想進行什麼操作?
30 a
31 請輸入要新增的數值:
32 419
33 數值新增成功
34 你想進行什麼操作?
35 a
36 請輸入要新增的數值:
37 129
38 數值新增成功
39 你想進行什麼操作?
40 a
41 請輸入要新增的數值:
42 322
43 數值新增成功
44 你想進行什麼操作?
45 a
46 請輸入要新增的數值:
47 544
48 數值新增成功
49 你想進行什麼操作?
50 a
51 請輸入要新增的數值:
52 367
53 數值新增成功
54 你想進行什麼操作?
55 l
56 mainQueue = { 539 264 372 424 419 129 322 544 367 }
57 你想進行什麼操作?
58 s
59 subQueue[0]:{ }
60 subQueue[1]:{ }
61 subQueue[2]:{ 372 322 }
62 subQueue[3]:{ }
63 subQueue[4]:{ 264 424 544 }
64 subQueue[5]:{ }
65 subQueue[6]:{ }
66 subQueue[7]:{ 367 }
67 subQueue[8]:{ }
68 subQueue[9]:{ 539 419 129 }
69 mainQueue = { 372 322 264 424 544 367 539 419 129 }
70 subQueue[0]:{ }
71 subQueue[1]:{ 419 }
72 subQueue[2]:{ 322 424 129 }
73 subQueue[3]:{ 539 }
74 subQueue[4]:{ 544 }
75 subQueue[5]:{ }
76 subQueue[6]:{ 264 367 }
77 subQueue[7]:{ 372 }
78 subQueue[8]:{ }
79 subQueue[9]:{ }
80 mainQueue = { 419 322 424 129 539 544 264 367 372 }
81 subQueue[0]:{ }
82 subQueue[1]:{ 129 }
83 subQueue[2]:{ 264 }
84 subQueue[3]:{ 322 367 372 }
85 subQueue[4]:{ 419 424 }
86 subQueue[5]:{ 539 544 }
87 subQueue[6]:{ }
88 subQueue[7]:{ }
89 subQueue[8]:{ }
90 subQueue[9]:{ }
91 mainQueue = { 129 264 322 367 372 419 424 539 544 }
92 排序完成
93 你想進行什麼操作?
94 l
95 mainQueue = { 129 264 322 367 372 419 424 539 544 }
96 你想進行什麼操作?
97 q

  (你可以將列舉子佇列的語句行55放入輸出更頻繁的地方如原行49後和52後,這樣更加能直觀地看到效果)

  從上面的輸出表可以看出,排序輪循一共進行了3次(外層,因為數值最大位數為3)。

  好,不多說了,大家領會精神。

 

 歡迎您移步我們的交流群,無聊的時候大家一起打發時間:Programmer Union

 或者通過QQ與我聯絡:點選這裡給我發訊息

 (最後編輯時間2013-11-22 22:38:04)

相關文章