计数排序

计数排序是一种线性排序算法,不需要进行比较,时间复杂度为O(n)。

它利用了一个数组,因为数组下标的增长是线性的,所以它就把自己的元素转换成新开辟数组的下标。可是下标都是非负数啊?数组当中的值有正有负啊。做一个简单的转化就行了:找到数组中最小元素,用元素值减去,这样一来,所有元素对应的下标就求出来了。(实际上感觉像是个映射函数?)下图中保存的是待排序数组:[-1,-5,-6,-2,1,2,8,2,1,8]

然后跟哈希排序的思路一样:这里。直接开辟一个对应的哈希数组,然后统计每个元素出现的次数。橙色标注出来的表示待排序数组中没有的元素(转换后的元素),自然就没有出现次数。这样可以看出来如果数组中元素差距很大,其实还是很浪费空间的,因为它新开辟数组的大小是待排序数组arr中max-min+1(8+6+1=15)。

此时把从count第二项开始count[i] += count[i-1];这样就可以确定不比该位置大的数据个数。

删去了count不需要用的元素(为了看的方便),而且你在最后遍历的时候,是遍历arr数组中的元素,用这个元素(转换后)去寻找count数组中其对应的元素,得到这个元素应该存放的位置。然后要重新开辟arr长度的数组psort(临时存放,最后再赋值给arr)。把当前元素存放到psort[pos]中。(用图说明把。。太绕了)

代码:

void CountSort(int *arr, int len) {
	if (arr == NULL)	return;
	int max = arr[0], min = arr[0];
	for (int i = 1; i < len; i++) {
		if (arr[i] > max)	max = arr[i];
		if (arr[i] < min)	min = arr[i];
	}
	int size = max - min + 1;
	int *count = (int*)malloc(sizeof(int)*size);
	memset(count, 0, sizeof(int)*size);
	for (int i = 0; i < len; i++) count[arr[i] - min]++;//包含了自己!
	for (int i = 1; i < size; i++)	count[i] += count[i - 1];
	int* psort = (int*)malloc(sizeof(int)*len);
	memset(psort, 0, sizeof(int)*len);
	for (int i = len - 1; i >= 0; i--) { 
		count[arr[i] - min]--;//要先把自己减去
		psort[count[arr[i] - min]] = arr[i];
	}
	for (int i = 0; i < len; i++) {
		arr[i] = psort[i];
	}
	free(count);
	free(psort);
	count = NULL;
	psort = NULL;
}

做LeetCode发现两个题也是利用了这种index和value映射的方法,当然题目给出了限制:出现的元素是1~n的。否则没办法正好映射。

442. 数组中重复的数据

448. 找到所有数组中消失的数字

这里并没有真的创建index数组,而是直接使用value的下标,为了方便看就显示出来,比如第0个元素4,对应的index应该是abs(4) - 1 =3,所以把value[index] 的对象7取反,来标记4已经出现了。核心思想就是这个,后面的变换很多,比如442题,重复的数据,因为一开始数字是从1~n的,所以肯定没负数,如果找到一个负数那么肯定是出现过一次的了。所以弄一个vector插入进去就可以了,插入的时候要注意是 :index + 1。比如442,出现了的都取反了,但是也有重复的,规定已经是负数的就不变换,所以最后消失的数字就是那些对应为正的数字。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值