多关键字排序
数据元素的关键字有多个,比如。其中,为最高位关键字,为最低位关键字。
两种常用的排序方法:
最高位优先法MSD:
这是一个递归过程,即先根据最高位关键字进行排序,按值的不同,将整个排序表分成若干个子表。然后分别对每个子表的数据元素根据关键字用最高位优先法进行排序。如此递归,直到对完成排序为止。
最低位优先法LSD:
首先根据最低位关键字对排序表中所有数据元素进行一趟排序,然后根据次低位关键字对上一趟排序的结果再排序,依次重复,知道按关键字完成最后一趟排序。这种排序方法每一趟都是对整个排序表操作,且需要采用稳定的排序方法。
基数排序
基数排序是一种典型的LSD排序方法,它利用“分配”和“收集”两种运算对单关键字进行排序。
这种方法把关键字k看成是一个d元组:。所谓基数就是每个分量的取值个数。比如984可以看成一个3元组(9,8,4),位数d=3,每一位有10中取值,所以基数radix=10。
分配:假设有radix个桶,对于d元组中一个分量,根据的取值,依次将所有数据元素分配到一个桶中。
收集:将每个桶中的数据元素合在一起,“收集起来”。。。
对于每一个分量,都进行分配和回收的操作,数据元素就排好序了。每一次分配,其实就是排序的过程,收集后,数据元素在该分量上已经排序好了。
下面的代码使用链表来存入数据元素,每次分配和收集都是对链表操作。
public class App
{
public static void main(String[] arg) {
// System.out.println(Arrays.toString(Linklist.Utils.decompose(123, 3)));
int[] array= {432,820,53,786,481,529,314,608,764,104};
Linklist linklist=new Linklist(array);
linklist.radixSort();
linklist.print();
}
}
class Linklist{
static class Utils{
/***
* 将一个整数的每一位都分解出来,返回含有每位的数组
* @param number 待分解的数组
* @param d 表示后d位要被分解出来
* @return 含有后d位的数组
*/
static int[] decompose(int number,int d) {
int[] key=new int[d];
for(int i=d;i>=1;i--) {
key[d-i]=(int) (number%Math.pow(10, i)/Math.pow(10, i-1));
}
return key;
}
}
class Node{
int[] key;
Node next;
public Node() {
// TODO Auto-generated constructor stub
}
public Node(int k,Node n) {
// key=k;
key=Utils.decompose(k, d);
next=n;
}
}
static int d=3;//关键字位数,比如123,012,关键字位数为3
Node head=new Node();
Node[] f=new Node[10];
Node[] e=new Node[10];
public Linklist(int[] array) {
Node pNode=head;
//建立单链表
for(int a:array) {
Node aNode=new Node(a,null);
pNode.next=aNode;
pNode=aNode;
}
}
void radixSort() {
for(int i=d-1;i>=0;i--) {
Node currentNode=head.next;
//装桶
while(currentNode!=null) {
if(f[currentNode.key[i]]==null) {
f[currentNode.key[i]]=currentNode;
e[currentNode.key[i]]=currentNode;
}else {
e[currentNode.key[i]].next=currentNode;
e[currentNode.key[i]]=currentNode;
}
currentNode=currentNode.next;
}
//回收
currentNode=head;
for(int j=0;j<10;j++) {
if(f[j]!=null) {
currentNode.next=f[j];
currentNode=e[j];
//清空,为下一轮做准备
f[j]=null;
e[j]=null;
}
}
currentNode.next=null;
}
}
public void print() {
Node currentNode=head.next;
while(currentNode!=null) {
StringBuilder stringBuilder=new StringBuilder();
for(int i=0;i<d;i++) {
stringBuilder.append(currentNode.key[i]);
}
System.out.print(stringBuilder.toString()+" ");
currentNode=currentNode.next;
}
}
}
输出结果:
053 104 314 432 481 529 608 764 786 820
算法需要重复执行d趟分配和收集。每趟分配需要分配数据元素到桶中,分配n次,收集的时候就radix个桶进行回收,因此时间复杂度为O(d(n+radix))。
上述可以看出,是通过数组来创建链表的,创建了n个节点,而桶的维护需要两个节点引用,一共radix个桶,因此空间复杂度为O(an+2radix),为啥我这多了个a呢,,,因为节点和节点引用占用的空间不一致,所以用a表明一下,权重关系要考虑下的。我书上的代码是用静态链表实现的,复杂度为O(n+2*radix),实现不一样,内存占用也是会有点不同的。