计数排序

本文内容和代码来自《漫画算法》
之前练习的冒泡排序、鸡尾酒排序、快速排序、堆排序都是基于元素比较和位置元素交换实现的,有一些特殊的排序并不基于元素比较,如计数排序桶排序基数排序
以计数排序来说,这种排序算法是利用数组下标来确定元素的正确位置的。
来看一个例子:
假设有20个随机整数,取值范围0-10,要求用最快的速度把这20个整数从小到大进行排序
建立一个长度为11的数组,下标0-10,元素全为0。
统计数组
假设20个数字如下所示
9,3,5,4,9,1,2,7,8,1,3,6,5,3,4,0,10,9,7,9
下面遍历这个20个元素的数字序列,每一个整数按其值对号入座,同时数组下标的元素进行加1操作。
例如第一个整数是9,那么下标为9的元素加1。
9对号入座
第二个整数是3,那么下标为3的元素加1。
3对号入座
继续遍历并修改数组…
最终当数列修改完毕,数组的状态如下。
最终状态
该数组的每一个下标位置的值代表数列中对应整数出现的次数。有了这个统计结果,排序就很简单了。直接遍历数组,输出数组元素的下标值,元素的值是几,就输出几次。
0 1 1 2 3 3 3 4 4 5 5 6 7 7 8 9 9 9 9 10
显然,现在输出的数列已经是有序的了。
这就是计数排序的基本过程,它适用于一定范围内的整数排序。在取值范围不是很大的情况下,它的性能甚至快过那些时间复杂度为O(nlogn)的排序

后面还有很多图片,就不比着书画了。最主要的还有当有重复数据的时候涉及到的一个顺序的处理。还是看一下原书,更好理解一点。除了重复数据顺序的问题,还有一个就是无需序列不从0开始的情况的处理。
最终的java实现如下

	int[] countSort(int[] arr) {
		// 1.得到数组的最大值、最小值,并算出差值d
		int max = 0, min = 0;
		for (int i = 0; i < arr.length; i++) {
			if (arr[i] > max) {
				max = arr[i];
			}
			if (arr[i] < min) {
				min = arr[i];
			}
		}
		int d = max - min;

		// 2.创建统计数组并统计对应元素的个数
		int[] countArr = new int[d + 1];
		for (int i = 0; i < arr.length; i++) {
			countArr[arr[i] - min]++;
		}
		// 3.统计数组变形,后面的元素等于前面的元素之和
		for (int i = 1; i < countArr.length; i++) {
			countArr[i] += countArr[i - 1];
		}
		// 4.倒序遍历原始数组,从统计数组找到正确的位置,输出到结果数组
		int[] sortedArr = new int[arr.length];
		for (int i = arr.length - 1; i >= 0; i--) {
			sortedArr[countArr[arr[i] - min] - 1] = arr[i];
			countArr[arr[i] - min]--;
		}
		return sortedArr;
	}
	
	@Test
	public void fun1() {
		// int[] arr=new int[]{95,94,91,98,99,90,99,93,91,92};
		int[] arr = new int[] { 49, 38, 65, 97, 76, 13, 27, 49 };
		int[] sortedArr = countSort(arr);
		System.out.println(Arrays.toString(sortedArr));
	}

操作原始数组运算量n,操作统计数列运算量为m
时间复杂度O(n+m)
不考虑结果数组和统计数组,
空间复杂度O(m)
它的时间复杂度是线性级的,然而它的局限性也非常严重
1.当数列最大和最小值差距过大时,并不适合用计数排序
例如给出20个随机整数,范围在0到1亿之间,需要创建1亿个元素的数组,不但严重浪费空间,而且时间复杂度也会随之升高。
2.当数列元素不是整数时,也不适合用计数排序
如果数列中的元素都是小数,如25.213,或0.00 000 001这样的数字,则无法创建对应的统计数组。这样显然无法进行计数排序。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值