//counting sort 计数排序 //参考算法导论8.2节 #include<cstdio> #include<cstring> #include<algorithm> #include<cassert> using namespace std; const int k=5; const int n=7; int a[n]={5, 5, 1, 2 , 5, 4, 1}; int b[n]; int c[k+1]; int main() { int maxn=0; memset(c, 0, sizeof c); for(int i=0;i<n;i++) { c[a[i]]++; maxn=max(maxn, a[i]); } assert(maxn<=k); //统计<=i的个数, c[i]就是最终i要放置的位置 for(int i=1;i<=maxn;i++) { c[i]+=c[i-1]; } for(int i=0;i<=maxn;i++) printf("%d ", c[i]); printf(" "); for(int i=n-1;i>=0;i--) { b[c[a[i]] - 1]=a[i];//从0开始 --c[a[i]]; } for(int i=0;i<n;i++) printf("%d ", b[i]); printf(" "); return 0; }
计数排序的总运行时间为θ(k + n),当k = O(n)时,运行时间为Θ(n)
优于比较排序的下界Ω(nlgn),因为在计数排序的过程中没有出现输入元素之间的比较,而是用了输入元素的实际值来确定它们在数组的位置。
额外插入排序算法的稳定。
排序算法的稳定的定义为:具有相同值的元素在输出数组中的相对次序与它们在输入数组中的次序相同。可参考维基
对于简单数据的排序,稳定性好像没什么作用,但是对于复杂数据(不只一项属性)的排序,稳定性很重要。
计数排序是稳定的排序,因此计数排序被用作基数排序算法的子过程。
练习8.2-2
在counting_sort过程中,假设将for(j = length; j >= 1; j--) 改为for(j = 1; j <= length; j++)。证明该算法仍能正常地工作,修改后的算法是稳定的吗?
计数排序算法不依赖数组A的顺序,所以仍能正常运行,而且结果正确。
但是修改后的算法不稳定。因为没修改前,在数组A里靠后的元素,在数组B也靠后(针对与元素值相同),如果修改后,结果会相反。违反了具有相同值的元素在输出数组中的相对次序与它们在输入数组中的次序相同,即稳定性。
练习8.2-4
给出一个算法,使之对于给定介于0和k之间的n个整数进行预处理,并能在O(1)时间内,回答出输入的整数中有多少个落在区间[a..b]内。你给出的算法的预处理时间应为Θ(n + k)。
用一个数组C,记录小于或等于其每个下标的值的元素个数。C[b] - C[a-1]为落在区间内的元素个数
Compute theCarray as is done in counting sort. The number of integers in the
range [a..b]isC[b]−C[a−1], where we interpretC[−1] as 0.