数据结构:排序—计数,桶,基数排序(五)

目录

一、计数排序

二、桶排序

三、基数排序


一、计数排序

基本思路:计数排序是不基于比较的排序方法,他是按照找出所排序序列的最大值和最小值,并且根据最大值和最小值的差值开辟一个数组:max-min+1。

然后,遍历待排序数组,将得到的值作为新数组的下标,我们每得到一个数就再让新数组这个数所对应的下标处的值加1。这样就知道了对应位置有多少的相同元素。

之后,我们遍历新数组,得到新数组的值,同时从头根据新数组的下标值依次更新原数组的值,这样我们就得到了我们升序的数据(如果是相同的元素需要根据新数组的值存放,每存放一个就将新数组的值减一,直到值为0,同理如果只有一个元素(没有相同的元素)值就为1,存放后也会等于0)。

注意:我们还有一点需要注意,如果我们要排序的数据0-9,我们创建的数组就是0-9的数组,但如果我们排序的是90-99的数,我们还是会创建0-9的数组,但这样我们就没有办法存储数据了,因此我们需要对他进行改进,那么我们该怎么做呢?

我们可以在取得原数组的值时,让他减去我们的最小值,像我们的90-90=0,91-90=1,这样我们就能存进数组里了。

但是,我们由于将他减去了最小值,但是我们在更新数组的时候,我们更新的值是我们新数组的下标,所以我们在更新的时候要加上最小值。

java 复制代码
public static void countSort(int[] arr){
        int min = arr[0];
        int max = arr[0];
        for(int i = 1;i<arr.length;i++){
            if(arr[i] < min){
                min = arr[i];
            }
            if(arr[i] > max){
                max = arr[i];
            }
        }
        int len = max-min+1;
        int[] countArr = new int[len];
        for(int i = 0;i<arr.length;i++){
            int index = arr[i]-min;
            countArr[index]++;
        }
        int k = 0;
        for (int i = 0; i < len; i++) {
            while(countArr[i] != 0){
                arr[k] = i+min;
                k++;
                countArr[i]--;
            }
        }
    }

二、桶排序

基本思路:桶排序是计数排序的延申,计数排序是可以看成每个相同的元素放入同一个桶中,而桶排序是将同一个范围的元素放入同一个桶中,然后排序每个桶中的元素,最后将不是空桶的桶中元素按照桶的顺序依次放回原数组中。

首先,我们还是求出最大值和最小值,根据他们的差值求出每个桶的范围大小:size = (mx - mn) / n + 1,n为数据的个数,需要保证至少有一个桶,故而需要加个1;(从最小值开始加上size就是第一个桶的范围,且范围为左闭右开)

之后,我们要根据桶的范围大小,求出桶的个数:cnt = (mx - mn) / size + 1,需要保证每个桶至少要能装1个数,故而需要加个1;

最后,我们要根据我们遍历到是数据求它应该在的桶编号:idx = (nums[i] - mn) / size。

java 复制代码
 //桶排序
    public static void bucketSort(int[] nums) {
        int n = nums.length;
        int mn = nums[0], mx = nums[0];
        // 找出数组中的最大最小值
        for (int i = 1; i < n; i++) {
            mn = Math.min(mn, nums[i]);
            mx = Math.max(mx, nums[i]);
        }
        int size = (mx - mn) / n + 1; // 每个桶存储数的范围大小,使得数尽量均匀地分布在各个桶中,保证最少存储一个
        int cnt = (mx - mn) / size + 1; // 桶的个数,保证桶的个数至少为1
        List<Integer>[] buckets = new List[cnt]; // 声明cnt个桶
        for (int i = 0; i < cnt; i++) {
            buckets[i] = new ArrayList<>();//创建桶
        }
        // 扫描一遍数组,将数放进桶里
        for (int i = 0; i < n; i++) {
            int idx = (nums[i] - mn) / size;//判断元素属于那个编号的桶中
            buckets[idx].add(nums[i]);
        }
        // 对各个桶中的数进行排序,这里用库函数快速排序
        for (int i = 0; i < cnt; i++) {
            buckets[i].sort(null); // 默认是按从小到大到排序
        }
        // 依次将各个桶中的数据放入返回数组中
        int index = 0;
        for (int i = 0; i < cnt; i++) {
            for (int j = 0; j < buckets[i].size(); j++) {
                nums[index++] = buckets[i].get(j);
            }
        }
    }

三、基数排序

基本思路:基数排序又是桶排序的延申,我们以整数为例,准备10个桶代表0-9,我们从整数的个位开始将对应的值放入对应编号的桶中,之后再将他们按照桶的顺序放回到原数组中,然后在从十位开始排,放入对应编号的桶中和放回到原数组中,之后从百位开始排,放入对应编号的桶中和放回到原数组中,如果对应的数没有百或十位数,就将他放到0号桶中。

最后当所有数的位都找完了后就代表我们已经将他排好序了。

java 复制代码
// 基数排序
public static void radixSort(int[] arr) {
	if (arr == null || arr.length < 2) {
		return;
	}
	radixSort(arr, 0, arr.length - 1, maxbits(arr));
}
	
// 计算最大位数
public static int maxbits(int[] arr) {
	int max = Integer.MIN_VALUE;
	for (int i = 0; i < arr.length; i++) {
		max = Math.max(max, arr[i]);
	}
	int res = 0;
	while (max != 0) {
		res++;
		max /= 10;
	}
	return res;
}

// 基数排序
public static void radixSort(int[] arr, int begin, int end, int digit) {
	final int radix = 10;
	int i = 0, j = 0;
	int[] count = new int[radix];
	int[] bucket = new int[end - begin + 1];
	// 依次遍历每个位数
	for (int d = 1; d <= digit; d++) {
		for (i = 0; i < radix; i++) {
			count[i] = 0;
		}
			
		// 统计数量
		for (i = begin; i <= end; i++) {
			j = getDigit(arr[i], d);
			count[j]++;
		}
			
		// 计算位置
		for (i = 1; i < radix; i++) {
			count[i] = count[i] + count[i - 1];
		}
			
		// 记录到对应位置
		for (i = end; i >= begin; i--) {
			j = getDigit(arr[i], d);
			bucket[count[j] - 1] = arr[i];
			count[j]--;
		}
		for (i = begin, j = 0; i <= end; i++, j++) {
			arr[i] = bucket[j];
		}
	}
}

// 获取位数数值
public static int getDigit(int x, int d) {
	return ((x / ((int) Math.pow(10, d - 1))) % 10);
}

好了,今天的分享就到这里了,还请大家多多关注我们下一篇见!

相关推荐
CoderCodingNo19 小时前
【GESP】C++四级真题 luogu-B4068 [GESP202412 四级] Recamán
开发语言·c++·算法
一个不知名程序员www19 小时前
算法学习入门---双指针(C++)
c++·算法
百锦再19 小时前
Vue Scoped样式混淆问题详解与解决方案
java·前端·javascript·数据库·vue.js·学习·.net
刘一说19 小时前
Spring Boot 启动慢?启动过程深度解析与优化策略
java·spring boot·后端
壹佰大多19 小时前
【spring如何扫描一个路径下被注解修饰的类】
java·后端·spring
百锦再19 小时前
对前后端分离与前后端不分离(通常指服务端渲染)的架构进行全方位的对比分析
java·开发语言·python·架构·eclipse·php·maven
Shilong Wang19 小时前
MLE, MAP, Full Bayes
人工智能·算法·机器学习
DokiDoki之父19 小时前
Spring—注解开发
java·后端·spring
Theodore_102219 小时前
机器学习(6)特征工程与多项式回归
深度学习·算法·机器学习·数据分析·多项式回归
知花实央l19 小时前
【算法与数据结构】拓扑排序实战(栈+邻接表+环判断,附可运行代码)
数据结构·算法