计数排序:counting-sort

计数排序是假设输入的数据都是0到k之间的整数,如果给定的数据集是有限的,那么排序可以在线性事件内完成。但是深入计数排序后会发现,计数排序有它的局限性,需要额外的存储空间,事件复杂度为O(n),但是空间复杂度上升到了O(n+k),如果数据集中整数的范围拉的很大,但是中间都是空出来的,有很大一部分数字没有出现,那么就不能能计数排序了。

先给出计数排序的基本算法(来自算法导论):

COUNTING-SORT(A, B, k)
1	for i <- to k
2		do C[i] <- 0
3	for j <- 1 to length[A]
4		do C[A[j]] <- C[A[j]] + 1 
5	for i <- 1 to k
6		do C[i] <- C[i] + C[i-1]
7	for j <- length[A] downto 1
8		do B[C[A[j]]] <- A[j]
9			C[A[j]] <- C[A[j]] - 1 

算法的关键步骤就是第4行,根据A[j]自身元素的值来统计它出现的次数,然后对于每个元素,确定小于其自身的个数,来求得自身最后在数组中的位置。比如有17个元素小于x,那么x最后在数组中出现的位置就是第十八个位置。但是如果有多个x的话,那就要像算法第6行的那样,计算出小于等于x元素的个数。然后从数组的最后一个元素开始遍历。假设有两个x,那么从后往前遍历,出现的肯定是倒数第一个x,这样把x放在计算出来的位置上。然后c[x]的值减少一个,当向前遍历的时候碰到第一个x的时候,正好放在c[x]值对应的位置上。从这点分析后,很容易看出,在原始数组中第一个x的位置在第二个x位置之前,在排序后,第一个x还是在第二个x位置之前。我们在讨论排序算法的时候都会讲到一个词,是否稳定,可以证明,计数排序是稳定的算法。而其从上面的伪代码中也可以看到,计数排序没有进行元素之间的比较,这就省去了很多时间。所以说基数排序算法在时间方面是高效的,同时也是稳定的。下面是自己根据上面的伪代码写的一个小程序。权当对计数排序算法的理解。感觉基数排序算法就像bit排序算法一样。

在算法导论上有一个习题,就是如果把第7行的 j 的值从1开始到length[A],那么程序还能继续执行,问题是算法还稳定吗?根据上面的分析,如果有多个x值,那么从前向后遍历第一个找到的x值被放在了相对其他x值得最后,所以算法就不是稳定的了。

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
void counting_sort(int *a, int *b, int len, int k) {
	int *record = (int*)malloc(sizeof(int) * (k+1));
	int i;
	if(!record)
		return;
	
	memset(record, 0, sizeof(int)*(k+1));
	
	for(i = 1; i <=len; i++)
		record[a[i]]++;
	for(i = 1; i <= k; i++)
		record[i] = record[i] +record[i-1];
	for(i = len; i >= 1; i--) {
		b[record[a[i]]] = a[i];
		record[a[i]]--; 
	}
}
int get_max_val(int *a, int len) {
	int max = a[1];
	int i;
	for(i = 2; i <= len; i++)
		if(a[i] > max)
			max = a[i];
		return max;
}
void main() {
	int a[] = {0, 2, 5, 3, 0, 2, 3, 0, 3};
	int len = sizeof(a)/sizeof(int);
	int max_val = 0;
	int *b = (int*)malloc(sizeof(int) * len);
	memset(b, 0, sizeof(int)*len);
	max_val = get_max_val(a, len-1);
	counting_sort(a, b, len-1, max_val);
	for(int i = 1; i < len; i++) {
		printf("%d\t", b[i]);
	}
	printf("\n");
}

你可能感兴趣的:(计数排序:counting-sort)