基数排序

多关键字排序

数据元素的关键字有多个,比如(k_{j}^{1},k_{j}^{2},...,k_{j}^{d})。其中,k^{1}为最高位关键字,k^{k}为最低位关键字。

两种常用的排序方法:

最高位优先法MSD:

这是一个递归过程,即先根据最高位关键字k^{1}进行排序,按k^{1}值的不同,将整个排序表分成若干个子表。然后分别对每个子表的数据元素根据关键字(k_{j}^{2},...,k_{j}^{d})用最高位优先法进行排序。如此递归,直到对k^{d}完成排序为止。

最低位优先法LSD:

首先根据最低位关键字k^{d}对排序表中所有数据元素进行一趟排序,然后根据次低位关键字k^{d-1}对上一趟排序的结果再排序,依次重复,知道按关键字k^{1}完成最后一趟排序。这种排序方法每一趟都是对整个排序表操作,且需要采用稳定的排序方法。

基数排序

基数排序是一种典型的LSD排序方法,它利用“分配”和“收集”两种运算对单关键字进行排序。

这种方法把关键字k看成是一个d元组:(k^{1},k^{2},...,k^{d})。所谓基数就是每个分量的取值个数。比如984可以看成一个3元组(9,8,4),位数d=3,每一位有10中取值,所以基数radix=10。

分配:假设有radix个桶,对于d元组中一个分量k^{j},根据k^{j}的取值,依次将所有数据元素分配到一个桶中。

收集:将每个桶中的数据元素合在一起,“收集起来”。。。

对于每一个分量,都进行分配和回收的操作,数据元素就排好序了。每一次分配,其实就是排序的过程,收集后,数据元素在该分量上已经排序好了。

下面的代码使用链表来存入数据元素,每次分配和收集都是对链表操作。

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),实现不一样,内存占用也是会有点不同的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值