计数排序是3大非比较排序(计数、基数及桶排序)之一,其基本原理是利用额外的存储空间(计数数组)对每个元素进行计数并将之存储到新的数组中(牺牲空间换时间)。此处的关键是计数数组的下标是原数据的元素值,即利用原数据的关键之进行索引(类似于hash表的索引)。其时间复杂度为Θ(n+k),即Θ(n)。注意,此处的计数排序时稳定排序算法(这是为什么step3从后往前扫描的原因)。
主要有3个步骤:
step1:记录待排序集合A中的每一个元素i具有的个数,存放到计数数组C[i]中
step2:在C的相应位置处确定不比该位置大的数据个数
step3:从大到小依次扫描原数据,将其存储到其应该存储的位置处
实现的C程序如下:参考了http://www.cppblog.com/shongbee2/archive/2009/04/24/80991.html
#include <stdio.h> #include <stdlib.h> //计数排序 int CountSort(int* pData, int nLen) { int* pCount = NULL; //保存记数数据的指针 pCount = (int*)malloc(sizeof(int) * nLen); //申请空间 int* pSort = NULL; //保存排序结果的指针 pSort = (int*)malloc(sizeof(int) * nLen); //申请空间 //step1:记录待排序集合A中的每一个元素i具有的个数,存放到C[i]中 for (int i = 0; i < nLen; ++i)//初始化记数为0 pCount[i] = 0; for (int i = 0; i < nLen; ++i)//记录排序记数,在排序的值相应记数加1 ++pCount[pData[i]]; //step2:确定不比该位置大的数据个数 for (int i = 1; i < nLen; ++i) pCount[i] += pCount[i - 1]; //不比他大的数据个数为他的个数加上前一个的记数。 //step3:从大到小依次扫描原数据,将其存储到其应该存储的位置处 for (int i = 0; i < nLen; ++i) { //把数据放在指定位置,因为pCount[pData[i]]的值就是不比他大数据的个数 //为什么要先减一,因为pCount[pData[i]]保存的是不比他大数据的个数中包括了 //他自己,此处的下标是从零开始的!所以要先减一。 --pCount[pData[i]]; //因为有相同数据的可能,所以要把该位置数据个数减一 pSort[pCount[pData[i]]] = pData[i]; } //排序结束,复制到原来数组中 for (int i = 0; i < nLen; ++i) pData[i] = pSort[i]; //最后要注意释放申请的空间 free(pCount); free(pSort); return 1; } int main() { int nData[10] = {8,6,3,6,5,8,3,5,1,0}; CountSort(nData, 10); for (int i = 0; i < 10; ++i) { printf("%d ", nData[i]); } printf("\n"); system("pause"); return 0; }
实现的C++程序如下(参考《算法导论》):
#include <iostream> #include <vector> using namespace std; void counting_sort(const vector<int>& A, vector<int>& B, int k) { //注意:此处A,B是从1开始存储元素的,如果从0开始的话,则要step3处应先-1 int i=0, j=0; vector<int> C(k+1,0); //step1:记录待排序集合A中的每一个元素i具有的个数,存放到C[i]中 //for (i=0; i<=k; ++i) //初始化 // C[i] = 0; for (j=1; j<A.size(); ++j) //C存储A中每个元素出现的个数,用A的关键字索引C C[A[j]] +=1; //step2:确定不比该位置大的数据个数 for (i=1; i<=k; ++i) //C[i]记录了<=C[i]的元素个数 C[i] += C[i-1]; //step3:从大到小依次扫描原数据,将其存储到其应该存储的位置处 for (j=A.size()-1; j>=1; --j) //从大到小扫描A,将A[j]存入到其应该存入的位置C[A[j]]中 { B[C[A[j]]] = A[j]; C[A[j]] -= 1; } } int main() { int i=0, n = 9,k = 5; vector<int> A(n,0), B(n,0); A[1] = 2; A[2] = 5; A[3] = 3; A[4] = 0; A[5] = 2; A[6] = 3; A[7] = 0; A[8] = 3; counting_sort(A,B,k); for (i=1; i<n; ++i) cout << B[i] << ' '; cout << endl; return 0; }
如果不考虑计数排序的稳定性,则由C中的下标就是A中元素的值可以直接利用C的下标及其相应的个数来给A重新排序,即可以不用额外的B空间。参考:http://www.cnblogs.com/eaglet/archive/2010/09/16/1828016.html,程序如下:
void counting_sort(vector<int> &A , int k) { vector<int> C(k+1);//初始化为0 int i; for ( i=1 ; i<A.size() ; i++)//C[i]包含等于i的个数 C[A[i]] = C[A[i]] + 1 ; int j=0; for (i=0 ; i<=k ; i++) { while (C[i]-- > 0) A[++j] = i; } }