基數排序(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)。
好,不多說了,大家領會精神。
(最後編輯時間2013-11-22 22:38:04)