基数排序简单Java实现

基数排序(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)。