基数排序及Java实现(学习笔记)
1.比较的对象是整数或者可以转化为整数
2.设整数最大位数为d,且是r进制(基数为r)的。
比如d=3,r=10的1个序列[001,100,200,201,210,211]
满足1,2条件的序列才能用基数排序
3.步骤
(1)首先初始化r个队列,[Q0,Q1,Q2,…Qr-1],设序列为[a0,a1,a2,…an-1]
(2)排序每一趟分为两个步骤,分配和收集,总趟数为r,设当前趟数为i
(3)分配:清空队列[Q0,Q1,Q2,…Qr-1],然后查看每个关键字的第i位,放入对应的队列中
取位的时候分为高位优先(MSD)和低位优先(LSD)
例如:a=123 如果是高位优先,则第1位是1;如果是低位优先,则第1位是3
(4)把队列中的元素首尾相接,连在一起
举例说明:
4.性能分析
假设元素已经节点化了,是用链式存储的
(1)空间复杂度:需要一个数组来存储r个队列的头尾指针,所以是O( r )
(2)时间复杂度:一共有d趟,对于每一趟的分配是O(n),收集是O( r )(链表的拼接本身是O(1)的),总时间复杂度为O( d*(n+r) )
当最大位数和进制为常数时,时间复杂度认为是O(n)
5.代码实现
(1)这里用int类型举个例子
(2)我的实现空间复杂度更大,因为我假定开始序列是顺序存储的,需要转化一下,需要额外的空间,达到了O( n ),这也比较符合实际情况吧。如果本来就是链式的,空间复杂度就是O( r )了
首先要定义一下链表,为了快速拼接,可以添加节点或者拼接链表
private static class Node {
// 数字
int elem;
// 数字按位分解
int[] bits;
// 下一个节点
Node next;
}
private static class LinkList {
Node head;
Node tail;
// 添加节点 时间复杂度:O(1)
void append(Node node) {
if (head == null && tail == null) {
head = node;
tail = node;
return;
}
tail.next = node;
tail = node;
}
// 链表拼接 时间复杂度:O(1)
void joint(LinkList linkList) {
if (linkList.head == null && linkList.tail == null) {
return;
}
if (head == null && tail == null) {
head = linkList.head;
tail = linkList.tail;
return;
}
if (this.tail != null) {
this.tail.next = linkList.head;
}
this.tail = linkList.tail;
}
// 清空,重复利用
void clear() {
head = null;
tail = null;
}
// 主要调试时候使用
public String toString() {
Node c = head;
StringBuilder sb = new StringBuilder();
while (c != tail) {
sb.append(c.elem + " ");
c = c.next;
}
if (tail != null) {
sb.append(c.elem + " ");
}
return sb.toString();
}
}
然后定义分解方法(decomposition)(可以把数字按位分解)和整数版本的求指数方法(pow)
// 求a^b,由于Math.pow()是浮点型不合适
private static int pow(int a, int b) {
int n = 1;
while (b-- > 0) {
n *= a;
}
return n;
}
// 按位分解
/***
*@param elem:待分解元素
*@param r:进制
*@param d:最大位数
*@return 分解结果
*/
private static int[] decomposition(int elem, int r, int d) {
int[] a = new int[d];
int index = 0;
for (int i = d - 1; i >= 0; i--) {
a[index] = elem / pow(r, i);
elem -= a[index] * pow(r, i);
index++;
}
return a;
}
最后写排序方法,先初始化转化为链表,然后是d趟的分配和收集
public static void radixSort(int[] array, int r, int d) {
// 初始化
LinkList rlt = new LinkList();
for (int elem : array) {
Node node = new Node();
node.elem = elem;
node.bits = decomposition(elem, r, d);
rlt.append(node);
}
LinkList[] buckets = new LinkList[r];
for (int i = 0; i < buckets.length; i++) {
buckets[i] = new LinkList();
}
// d趟
for (int i = d - 1; i >= 0; i--) {
// 分配
Node c = rlt.head;
while (c != rlt.tail) {
buckets[c.bits[i]].append(c);
c = c.next;
}
if (c != null) {
buckets[c.bits[i]].append(c);
}
rlt.clear();
// 收集
for (int j = 0; j < buckets.length; j++) {
LinkList linkList = buckets[j];
rlt.joint(linkList);
linkList.clear();
}
}
Node c = rlt.head;
int index = 0;
while (c != rlt.tail) {
array[index++] = c.elem;
c = c.next;
}
if(c!=null){
array[index++] = c.elem;
}
}
JUnit单元测试代码(主函数中运行效果相同):
@Test
public void testRadixSort() {
int[] a = {
211, 210, 201, 200, 100, 1};
System.out.println(Arrays.toString(a));
radixSort(a, 10, 3);
System.out.println(Arrays.toString(a));
}
控制台结果:
转载请注明出处,谢谢合作