算法-基数排序的实现

基数排序是对桶排序的一个扩展,是一个典型的以空间换时间的算法,其算法的实现附带着额外的空间开销

核心算法如下:(图上收集个位那里19应该放到下标为9的桶,图片有误)
链表实现的基数排序
我们需要借助10个桶来完成排序(可以用二维数组,也可以用链表数组来实现)

以图上待排序的数组{135,242,192,93,345,11,24,19}为例来说明这个算法的实现过程

  1. 初始化10个桶,每个桶的容量为待排序数组长度8,用二维数组来实现

  2. 数组第一轮遍历,取出各个元素的个位数,放入相应的桶: 135取出5,放入下标为5的桶中:bucket[5] = 135 242取出2,bucket[2]=242

  3. 数组第一轮遍历完成后,桶中已经存放了对应的元素[{0,0,0,0,0,0,0,0},{11,0,0,0,0,0,0,0},{242,192,0,0,0,0,0,0},{93,0,0,0,0,0,0,0},{24,0,0,0,0,0,0,0},{135,345,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{19,0,0,0,0,0,0,0}]

  4. 把这些桶中的元素按顺序重新放回待排序数组中,得到最新数组为{11,242,192,93,24,135,345,19}

  5. 清空每个桶中的元素

  6. 数组第二遍遍历,取出各个元素的十位数,放入相应的桶: 11取出1,bucket[1] = 11 242取出4,bucket[4] = 242 192取出9,bucket[9] = 192

  7. 第二遍遍历结束后,把这些桶中的元素按顺序重新放回待排序数组中,得到最新数组为{11,19,24,135,242,345,192,93}

  8. 清空每个桶中的元素

  9. 数组第三遍遍历,取出各个元素的百位数… … 直到循环了最长位数(该数组里边元素最长位数为百位,所以需要循环遍历三次数组)遍后数组就有序了

算法代码(桶用二维数组实现)


public class BaseNumberSortByArray {
	
	private static final int BUCKET_SIZE = 10;//桶的数量
	
	private static int[] myArr; //原始数组
	private static int[][] bucket;//桶
	private static int[] elementCount;//每个桶真实所存放的元素数量
	
	
	private BaseNumberSortByArray() {}
	
	public static BaseNumberSortByArray instance(int[] arr) {
		myArr = arr;
		initBucket(arr.length);
		return new BaseNumberSortByArray();
	}
	
	/**
	 * 
	 * @Title: initBucket
	 * @Description: 初始化桶和每个桶目前所存放的元素数量为0
	 * @param: @param length 每个桶的容量
	 * @return: void
	 */
	private static void initBucket(int length) {
		bucket = new int[BUCKET_SIZE][length];
		elementCount = new int[BUCKET_SIZE];
	}
	
	
	/**
	 * 
	 * @Title: getMaxLength
	 * @Description: 获取所有元素中位数的最大值
	 * @return: int 位数的最大值
	 */
	private int getMaxLength() {
		int length = myArr.length;
		int max = myArr[0];
		for(int i = 1; i < length; i++) {
			if(max < myArr[i])max = myArr[i];
		}
		return (max + "").length();
	}
	
	/**
	 * 
	 * @Title: baseNumberSort
	 * @Description: 基数排序入口
	 * @return: void
	 */
	public void baseNumberSort() {
		/**
		 * 算法:
		 * 1.循环获取各个元素的个位数,该个位数作为下标将元素存放入相应的桶中(如元素为32,个位数为2,那么就将32存放入下标为2的桶中)
		 * 2.按顺序遍历所有桶,将桶中的元素按顺序取出放回原始数组中
		 * 3.清空桶中的元素计数
		 * 4.循环获取各个元素的百位数,该百位数作为下标将元素存放入相应的桶中
		 * 5. ....依次循环,直到取到最长位数
		 */
		int maxLength = getMaxLength();
		int myArrSize = myArr.length;
		int tempNumber = -1;//取出的当前位数值
		
		//1,4...
		for(int i = 0; i < maxLength; i++) {
			for(int j = 0; j < myArrSize; j++) {
				tempNumber = (myArr[j] / (int)Math.pow(10, i)) % 10;//取出当前位数
				bucket[tempNumber][elementCount[tempNumber]] = myArr[j];//该位数作为下标将元素存放入相应的桶中
				elementCount[tempNumber]++;//桶中元素计数加一
			}
			
			//2....
			//按顺序遍历所有桶,将桶中的元素按顺序取出放回原始数组中
			
			int k = 0;//遍历桶元素存入原始数组所使用的指向原始数组的当前下标
			//遍历每个桶
			for(int ii = 0; ii < BUCKET_SIZE; ii++) {
				//遍历每个桶中的元素
				for(int jj = 0; jj < elementCount[ii]; jj++) {
					myArr[k] = bucket[ii][jj];
					k++;
				}
				
				//3...
				//当前桶中元素的计数置为0
				elementCount[ii] = 0;	
				
			}
		}
		
	}

}


算法代码(桶用链表数组实现)

class BucketNode{
	int num;
	BucketNode node;
	BucketNode(int num){
		this.num = num;
	}
	
}
public class BaseNumberSortByLinkList {
	
	private static final int BUCKET_SIZE = 10;//桶的数量
	
	private static int[] myArr; //原始数组
	
	private static BucketNode[] bucket;//桶
	
	private BaseNumberSortByLinkList() {}
	
	public static BaseNumberSortByLinkList instance(int[] arr) {
		myArr = arr;
		initBucket();//初始化桶
		return new BaseNumberSortByLinkList();
	}

	
	private static void initBucket() {
		bucket = new BucketNode[BUCKET_SIZE];
		for(int i = 0; i < BUCKET_SIZE; i++) {
			bucket[i] = new BucketNode(-1);
		}
	}
	
	/**
	 * 
	 * @Title: getEndBucketNode
	 * @Description: 获得指定桶的最后一个元素
	 * @param: @param index 指定的桶的下标
	 * @return: BucketNode 最后一个元素
	 */
	private BucketNode getEndBucketNode(int index) {
		BucketNode bucketNode = bucket[index];
		while(bucketNode.node != null)bucketNode = bucketNode.node;
		return bucketNode;
	}
	
	
	/**
	 * 
	 * @Title: baseNumberSort
	 * @Description: 基数排序入口
	 * @return: void
	 */
	public void baseNumberSort() {
		/**
		 * 算法:
		 * 1.循环获取各个元素的个位数,该个位数作为下标将元素存放入相应的桶中(如元素为32,个位数为2,那么就将32存放入下标为2的桶中)
		 * 2.按顺序遍历所有桶,将桶中的元素按顺序取出放回原始数组中
		 * 3.清空桶中的元素计数
		 * 4.循环获取各个元素的百位数,该百位数作为下标将元素存放入相应的桶中
		 * 5. ....依次循环,直到取到最长位数
		 */
		int maxLength = getMaxLength();
		int myArrSize = myArr.length;
		int tempNumber = -1;//取出的当前位数值
		
		//1,4...
		for(int i = 0; i < maxLength; i++) {
			for(int j = 0; j < myArrSize; j++) {
				tempNumber = (myArr[j] / (int)Math.pow(10, i)) % 10;//取出当前位数
				BucketNode endBucketNode = getEndBucketNode(tempNumber);
				endBucketNode.node = new BucketNode(myArr[j]);//该位数作为下标将元素存放入相应的桶中
			}
			
			//2....
			//按顺序遍历所有桶,将桶中的元素按顺序取出放回原始数组中
			BucketNode bucketNode = null;
			int k = 0;//遍历桶元素存入原始数组所使用的指向原始数组的当前下标
			//遍历每个桶
			for(int ii = 0; ii < BUCKET_SIZE; ii++) {
				//遍历每个桶中的元素
				bucketNode = bucket[ii].node;
				while(bucketNode != null) {
					myArr[k] = bucketNode.num;
					k++;
					bucketNode = bucketNode.node;
				}
				
				//3...
				//清空当前桶中的元素	
				clearBucket(ii);
				
			}
		}
		
	}
	
	/**
	 * 
	 * @Title: clearBucket
	 * @Description: 清空指定桶的所有元素
	 * @param: @param index 指定桶的下标
	 * @return: void
	 */
	private void clearBucket(int index) {
		bucket[index].node = null;
	}
	
	/**
	 * 
	 * @Title: getMaxLength
	 * @Description: 获取所有元素中位数的最大值
	 * @return: int 位数的最大值
	 */
	private int getMaxLength() {
		int length = myArr.length;
		int max = myArr[0];
		for(int i = 1; i < length; i++) {
			if(max < myArr[i])max = myArr[i];
		}
		return (max + "").length();
	}
	
	
	

}

时间复杂度:O(n*k)
空间复杂度:O(n+k)
稳定性:稳定
ps: n为数据规模, k为桶的个数

基数排序的缺点:
  • 当数据规模很大时,可能会产生oom问题,没有足够的内存空间可以初始化桶
  • 基础的基数排序不支持负数排序

源码地址github地址

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值