基数排序(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) + " "); 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 += " "; 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 += "} "; 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("你想进行什么操作? "); 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("请输入要添加的数值: "); 157 inputInfo = stdin.readLine().trim(); 158 int num = Integer.parseInt(inputInfo); 159 sort1.add(num); 160 System.out.print("数值添加成功 "); 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("排序完成 "); 170 break; 171 case '?': // 显示帮助 172 printMenu(); 173 break; 174 default: 175 System.out.print("未知指令 "); 176 break; 177 } 178 } else { 179 System.out.print("未知指令 "); 180 } 181 } while (input1 != 'Q' || line.length() != 1); 182 } catch (IOException exception) { 183 System.out.print("IO Exception "); 184 } 185 } 186 187 /** 打印控制台界面(菜单) */ 188 public static void printMenu() { 189 System.out.print("选项 动作 " + "------ ------ " 190 + "A 添加一个数值 " + "L 列举队列 " 191 + "Q 退出 " + "S 排序数据 " 192 + "? 显示帮助 "); 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)