计数排序是一个类似于桶排序的排序算法,其优势是对已知数量范围的数组进行排序。它创建一个长度为这个数据范围的数组C,C中每个元素记录要排序数组中对应记录的出现个数。这个算法于1954年由
Harold H. Seward 提出。
这里最大值为3,最小值为0,那么我们创建一个数组C,长度为4.
然后一趟扫描数组A,得到A中各个元素的总数,并保持到数组C的对应单元中。
比如0 的出现次数为2次,则 C[0] = 2;1 的出现次数为4次,则C[1] = 4
由于C 是以A的元素为下标的,所以这样一做,A中的元素在C中自然就成为有序的了,这里我们可以知道 顺序为 0,1,3 (2 的计数为0)
然后我们把这个在C中的记录按每个元素的计数展开到输出数组B中,排序就完成了。
也就是 B[0] 到 B[1] 为0 B[2] 到 B[5] 为1 这样依此类推。
这种排序算法,依靠一个辅助数组来实现,不基于比较,算法复杂度为 O(n) ,但由于要一个辅助数组C,所以空间复杂度要大一些,由于计算机的内存有限,这种算法不适合范围很大的数的排序。
注:基于比较的排序算法的最佳平均时间复杂度为 O(nlogn)
上面这段引自麻省理工大学计算机算法教材的技术排序部分,我不做翻译了。这个就是这个算法的典型解法,我把它作为方案1.
方案1
public static void Sort(int[] A, out int[] B, int k)
{
Debug.Assert(k > 0);
Debug.Assert(A != null);
int[] C = new int[k + 1];
B = new int[A.Length];
for (int j = 0; j < A.Length; j++)
{
C[A[j]]++;
}
for (int i = 1; i <= k; i++)
{
C[i] += C[i-1];
}
for (int j = A.Length - 1; j >= 0; j--)
{
B[C[A[j]]-1] = A[j];
C[A[j]]--;
}
}
方案2
public static void Sort(int[] A, int k) { Debug.Assert(k > 0); Debug.Assert(A != null); int[] C = new int[k + 1]; for (int j = 0; j < A.Length; j++) { C[A[j]]++; } int z = 0; for (int i = 0; i <= k; i++) { while (C[i]-- > 0) { A[z++] = i; } } }
由于C数组下标 i 就是A 的值,所以我们不需要保留A中原来的数了,这个代码减少了一个数组B,而且要比原来的代码简化了很多。
和快速排序的速度比较
int[] A = new int[1000000];
int[] B = new int[1000000];
Random rand = new Random();
for (int i = 0; i < A.Length; i++)
{
A[i] = rand.Next(0, 100);
}
A.CopyTo(B, 0);
Stopwatch sw = new Stopwatch();
sw.Start();
Array.Sort(B);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
CountingSort.Sort(A, 100);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
来源:http://www.cnblogs.com/eaglet/archive/2010/09/16/1828016.html