以前研究的各种排序算法,都是通过比较数据大小的方法对欲排数据序列进行排序处理过程,而基数排序却不再相同。
那么,基数排序是采用怎样策略进行数据排序的呢?
简略概述:基数排序是通过“分配”和“收集”过程来实现排序。而这个思想该如何理解呢?请看以下例子:
(1)假设有欲排数据序列如下所示:
73 22 93 43 55 14 28 65 39 81
首先,根据每个数据个位数的数值,在遍历数据时将它们各自分配到编号0至9的桶(个位数值与桶号一一对应)中。
分配结果(逻辑想象)如下图所示:
分配结束后。接下来将所有桶中(由顶至底)所盛数据按照桶号由小到大依次重新收集串起来,得到如下仍然无序的数据序列:
81 22 73 93 43 14 55 65 28 39
接着,再进行一次分配,这次根据每个数据十位数的数值来分配(原理同上),分配结果(逻辑想象)如下图所示:
分配结束后。接下来再将所有桶中(由顶至底)所盛的数据(原理同上)依次重新再收集串接起来,得到如下的数据序列:
14 22 28 39 43 55 65 73 81 93
至此,观察可以看到:原无序数据序列已经变成有序的数据序列,即排序完毕。
如果排序的数据序列有三位数以上的数据,则重复进行以上的动作直至最高位数为止。
追问:观察原无序数据序列中73 93 43 三个数据的顺序,在经过第一次(按照个位数值,它们三者应该是在同一个桶中)分配之后,
在桶中顺序由底至顶应该为73 93 43(即就是装的迟的在最上面,对应我们上面的逻辑想象应该是43 93 73),对吧?这个应该可以想明白吧?理论上应该是这样的。
但是、但是、但是分配后很明显在3号桶中三者的顺序刚好相反。这点难道你没有发现吗?或者是发现了觉得不屑谈及(算我贻笑大方)?
其实这个也正是基数排序稳定性的原因(分配时由末位向首位进行,即逆向遍历),请看下文的详细分析。
再思考一个问题:既然我们可以从最低位到最高位进行如此的分配收集,那么是否可以由最高位到最低位依次操作呢? 答案是完全可以的。
基于两种不同的排序顺序,我们将基数排序分为LSD(Least significant digital)或 MSD(Most significant digital),
LSD的排序方式由数值的最右边(低位)开始,而MSD则相反,由数值的最左边(高位)开始。
注意一点:LSD的基数排序适用于位数少的数列,如果位数多的话,使用MSD的效率会比较好。
MSD的方式与LSD相反,是由高位数为基底开始进行分配,但在分配之后并不马上合并回一个数组中,而是在每个“桶子”中建立“子桶”,将每个桶子中的数值按照下一数位的值分配到“子桶”中。
在进行完最低位数的分配后再合并回单一的数组中。
(2)我们把扑克牌的排序看成由花色和面值两个数据项组成的主关键字排序。
要求如下:
花色顺序:梅花<方块<红心<黑桃
面值顺序:2<3<4<…<10<J<Q<K<A
那么,若要将一副扑克牌排成下列次序:
梅花2,…,梅花A,方块2,…,方块A,红心2,…,红心A,黑桃2,…,黑桃A。
有两种排序方法:
<1> 先按花色分成四堆,把各堆收集起来;然后对每堆按面值由小到大排列,再按花色从小到大按堆收叠起来。----称为"最高位优先"(MSD)法。
<2> 先按面值由小到大排列成13堆,然后从小到大收集起来;再按花色不同分成四堆,最后顺序收集起来。----称为"最低位优先"(LSD)法。
代码实现:
1.不使用栈
package com.sxt.b;
import java.util.Arrays;
/**
* 基数排序
* @author 13631
*
*/
public class RadixSort {
public static void main(String[] args) {
int[] arr =new int[] {23,6,189,45,9,287,56,1,798,34,65,652,5};
radixSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void radixSort(int[] arr) {
//最大数的最高位决定了比较的次数
int max=Integer.MIN_VALUE;
for(int i=0;i<arr.length;i++)
{
if(arr[i]>max) {
max=arr[i];
}
}
//计算最大数字是几位
int maxLength =(max+"").length();
//用于存放某次(比较次数小于最大位数)按某位比较的结果
int[][] temp=new int[10][arr.length];
//用于存放相应数组(0-9)中存放数字的数量
int[] counts =new int[10];
//根据最大长度决定比较的次数
for(int i=0,n=1;i<maxLength;i++,n*=10) {
//把每个数字分别计算余数
for(int j=0;j<arr.length;j++) {
//计算余数
int ys=arr[j]/n%10;
//把当前遍历的数放入指定数组中,余数是几就是放在第几行,这一行的元素个数正好是此时的下标
temp[ys][counts[ys]]=arr[j];
//记录数量
counts[ys]++;
}
//接下来拿出已经排好的元素,把值赋给数组arr
int index=0;
for(int k=0;k<counts.length;k++) {
//从当前有数据的行中取值
if(counts[k]!=0) {
for(int l=0;l<counts[k];l++) {
arr[index]=temp[k][l];
//记录下一个位置
index++;
}
//将数组列元素个数标记置为0
counts[k]=0;
}
}
}
}
}
2.使用栈
package com.sxt.queue;
//建立一个栈:
public class MyQueue {
int[] elements;
public MyQueue() {
elements=new int[0];
}
//入队
public void add(int element) {
// 创建一个新的数组
int[] newArr = new int[elements.length + 1];
// 把原数组中的元素复制到新数组中
for (int i = 0; i < elements.length; i++) {
newArr[i] = elements[i];
}
// 把添加的元素放入新数组中
newArr[elements.length] = element;
// 使用新数组替换旧数组
elements = newArr;
}
//出队
public int poll() {
if(elements.length==0) {
throw new RuntimeException("stack is empty");
}
//把数组中的第0个元素取出来
int element = elements[0];
//创建一个新的数组
int[] newArr = new int[elements.length-1];
//复制原数组中的元素到新数组中
for(int i=0;i<newArr.length;i++) {
newArr[i]=elements[i+1];
}
//替换数组
elements=newArr;
return element;
}
//判断队列是否为空
public boolean isEmpty() {
return elements.length==0;
}
}
package com.sxt.b;
import java.util.Arrays;
import com.sxt.queue.MyQueue;
public class RadixQueueSort {
public static void main(String[] args) {
int[] arr =new int[] {23,6,189,45,9,287,56,1,798,34,65,652,5};
radixSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void radixSort(int[] arr) {
//最大数的最高位决定了比较的次数
int max=Integer.MIN_VALUE;
for(int i=0;i<arr.length;i++)
{
if(arr[i]>max) {
max=arr[i];
}
}
//计算最大数字是几位
int maxLength =(max+"").length();
//建一个栈用于存放某次(比较次数小于最大位数)按某位比较的结果,
MyQueue[] temp = new MyQueue[10];
//为队列数组赋值
for(int i=0;i<temp.length;i++) {
temp[i]=new MyQueue();
}
//根据最大长度决定比较的次数
for(int i=0,n=1;i<maxLength;i++,n*=10) {
//把每个数字分别计算余数
for(int j=0;j<arr.length;j++) {
//计算余数
int ys=arr[j]/n%10;
//把当前遍历的数放入指定数组中,余数是几就是放在第几行,这一行的元素个数正好是此时的下标
temp[ys].add(arr[j]);
}
//接下来拿出已经排好的元素,把值赋给数组arr
int index=0;
for(int k=0;k<temp.length;k++) {
//循环取出元素
while(!temp[k].isEmpty()) {
//取出元素
arr[index] = temp[k].poll();
//记录下一个位置
index++;
}
}
}
}
}