排序问题之计数排序
排序问题
算法问题的基础问题之一,便是排序问题:
输入:n个数的一个序列,<a1, a2,..., an>。
输出:一个排列<a1',a2', ... , an'>,满足a1' ≤ a2' ≤... ≤ an' 。(输出亦可为降序,左边给出的例子为升序)
一.算法描述
计数排序假设输入一个长度为n的序列,每一个都是0到k之间的整数,其中k为某个常数整数。当k=O(n)时,排序的时间复杂度为O(n)。
对于A[1,n],我们首先分配一个输出结果的空间B[1,n],然后还需要一个计数数组C[0,k]。首先我们遍历原序列A[1,n],将区间[0,k]内每一个整数出现的次数累积统计到C中,然后从头开始将C中的每一个位置上的计数与前一位置累加。最后从尾至头遍历A,将对应的值val存放到B[C[val]]中,并且C[val]减一,一趟遍历后完成排序。
二.代码实现
下面是插入排序的C++实现:
#include<iostream> #include<vector> using namespace std; //计数排序 void countingsort(vector<int> &v, int k) { //初始化 vector<int> temp(v.size(),0); vector<int> c(k + 1,0); //遍历一遍原数组,计数与c[k]中 for(int i = 0; i < v.size(); i++) { c[v[i]] = c[v[i]] + 1; } //将c[k]中的计数累和 for(int i = 1; i <= k + 1; i++) { c[i] = c[i] + c[i-1]; } //将计数的元素存到temp中 for(int j = v.size() - 1; j >= 0; j--) { temp[c[v[j]] - 1] = v[j]; c[v[j]]--; } //把排序后的temp赋给v v = temp; } int main() { int arr[] = { 1, 2, 6, 4, 3, 5, 2, 6, 3, 4, 5 ,6}; vector<int> v(arr, arr + sizeof(arr)/sizeof(int)); int k = 6; countingsort(v, k); copy (v.begin(), v.end(), ostream_iterator<int> (cout, " ")); cout << endl; }
三.算法分析
(1)时间复杂度
由于我们要遍历两次A和一次C,时间复杂度为θ(k + n)。当k=θ(n)时,使用计数排序的时间复杂度就为θ(n),同样也是o(n)。
(2)稳定性
稳定。对于A中的同值元素x与x,由于我们往B中填入元素时是对A从尾至头遍历的,所以位于后面的x对应的C[x]值会更大,因而会填入到B中更靠后的位置,所以B中的同值元素顺序与A中相同,所以计数排序是稳定的排序算法。
(3)适合范围
知道序列中的最大最小值,且最值等于θ(n)时适用。