`

计数排序(时间复杂度为O(n)的排序)

 
阅读更多

1、http://hi.baidu.com/hustor/blog/item/086a5701c126b4cd267fb524.html(转)

 

计数排序是一种算法复杂度 O(n) 的排序方法,适合于小范围集合的排序。比如100万学生参加高考,我们想对这100万学生的数学成绩(假设分数为0到100)做个排序。我们如何设计一个最高效的排序算法。本文不光给出计数排序算法的传统写法,还将一步步深入讨论算法的优化,直到时间复杂度和空间复杂度最优。

先看看计数排序的定义

Counting sort (sometimes referred to as ultra sort or math sort[1]) is a sorting algorithm which (like bucket sort) takes advantage of knowing the range of the numbers in the array to be sorted (array A). It uses this range to create an array C of this length. Each index i in array C is then used to count how many elements in A have the value i; then counts stored in C can then be used to put the elements in A into their right position in the resulting sorted array. The algorithm was created by Harold H. Seward in 1954.

计数排序是一个类似于桶排序的排序算法,其优势是对已知数量范围的数组进行排序。它创建一个长度为这个数据范围的数组C,C中每个元素记录要排序数组中对应记录的出现个数。这个算法于1954年由 Harold H. Seward 提出。

下面以示例来说明这个算法

假设要排序的数组为 A = {1,0,3,1,0,1,1}

这里最大值为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)

 

Counting sort
Depends on a key assumption: numbers to be sorted are integers in{0, 1, . . . , k}.
Input: A[1 . . n], where A[ j ] ∈ {0, 1, . . . , k} for j = 1, 2, . . . , n. Array A and
values n and k are given as parameters.
Output: B[1 . . n], sorted. B is assumed to be already allocated and is given as a
parameter.
Auxiliary storage: C[0 . . k]
8-4 Lecture Notes for Chapter 8: Sorting in Linear Time
COUNTING-SORT(A, B, n, k)
for i ← 0 to k
do C[i ] ← 0
for j ← 1 to n
do C[A[ j ]] ← C[A[ j ]] + 1
for i ← 1 to k
do C[i ] ← C[i ] + C[i − 1]
for j ← n downto 1
do B[C[A[ j ]]] ← A[ j ]
C[A[ j ]] ← C[A[ j ]] − 1
Do an example for A = 21, 51, 31, 01, 22, 32, 02, 33
Counting sort is stable (keys with same value appear in same order in output as
they did in input) because of how the last loop works.

上面这段引自麻省理工大学计算机算法教材的技术排序部分,我不做翻译了。这个就是这个算法的典型解法,我把它作为方案1.

这个算法的实际扫描次数为 n+k (不包括写的次数)

方案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]]--;

            }

 

        }

 

上面代码是方案1 的解法,也是计数排序算法的经典解法,麻省的教材上也是这样解。不过这个解法并不是最优的,因为空间复杂度还应该可以优化,我们完全可以不要那个输出的数组B,直接对A进行排序。在继续看方案2之前,我建议大家先自己思考一下,看看是否有办法省略掉数组B

 

方案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 = newint[1000000];int[] B = newint[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);

 

输出结果

134 //快速排序
18   //计数排序

可见计数排序要比快速排序快将近6倍左右。


2、计数排序算法:http://www.cnblogs.com/brucezhao/articles/996846.html(转)

 

 

分享到:
评论

相关推荐

    基数排序,快于sort的O(n)排序

    时间复杂度达到O(n)的不同于sort给予比较的O(nlogn)排序,是基于计数的一种线性排序方法,效率非常优秀。

    AlgorithmPractice

    O(1) 归并排序时间复杂度:O(nlogn) 空间复杂度:O(n) 计数反转时间复杂度:O(nlogn) 空间复杂度:O(n) 基于自顶向下归并排序。 唐叶乘法不打算实施这个。 施特拉森朴素矩阵乘法分治矩阵乘法时间复杂度:O(n^3) 空间...

    C++ 计数排序实例详解

     计数排序在对于一定范围内的整数排序时,时间复杂度为O(N+K) (K为整数在范围)快于任何比较排序算法,因为基于比较的排序时间复杂度在理论上的上下限是O(N*log(N))。 缺点:  计数排序是一种牺牲空间换取时间的...

    计数排序算法的C语言实现

    以前看过网上的一篇关于一种号称时间复杂度能达到O(n)的不用比较就可以实现排序的算法——计数排序法,这是自己用C写的一个实现,大家可以参考下,欢迎指证。

    JS实现的计数排序与基数排序算法示例

    计数排序就是简单的桶排序,一个桶代表数组中一个数出现的个数,所以需要一个和数组数字范围一样大的辅助数组,一般用在范围小于100的排序,时间复杂度为O(n),空间复杂度为数组的数字范围。 /** * 范围在 start -...

    leetcode切割分组-java_algorithm:排序算法演示

    leetcode切割分组 java_algorithm this is a sorting algorithm demo. created by InterlliJ. ...平均时间复杂度 O(n^2) 空间复杂度 O(1) ...平均时间复杂度 O(n ...n)^2) 空间复杂度 ...计数排序 | 用一个计算器

    C++算法实现代码集.rar

    在冒泡排序之类的排序中,问题规模为n,又因为需要比较n次,所以平均时间复杂度为O(n²)。在归并排序、快速排序之类的排序中,问题规模通过分治法消减为logN次,所以时间复杂度平均O(nlogn)。 计数排序、基数排序、...

    Python实现的计数排序算法示例

    计数排序是一种非常快捷的稳定性强的排序方法,时间复杂度O(n+k),其中n为要排序的数的个数,k为要排序的数的组大值。计数排序对一定量的整数排序时候的速度非常快,一般快于其他排序算法。但计数排序局限性比较大,...

    heapsort

    排序算法大体可分为两种: 一种是比较排序,时间复杂度O(nlogn) ~ O(n^2),主要有:冒泡排序,选择排序,插入排序,归并排序,堆排序,快速排序等。 另一种是非比较排序,时间复杂度可以达到O(n),主要有:计数排序...

    mergesort

    排序算法大体可分为两种: 一种是比较排序,时间复杂度O(nlogn) ~ O(n^2),主要有:冒泡排序,选择排序,插入排序,归并排序,堆排序,快速排序等。 另一种是非比较排序,时间复杂度可以达到O(n),主要有:计数排序...

    conutingsort

    排序算法大体可分为两种: 一种是比较排序,时间复杂度O(nlogn) ~ O(n^2),主要有:冒泡排序,选择排序,插入排序,归并排序,堆排序,快速排序等。 另一种是非比较排序,时间复杂度可以达到O(n),主要有:计数排序...

    桶排序算法的理解及C语言版代码示例

    理解: 桶排序是计数排序的变种,把计数排序中相邻的m个”...桶排序的平均时间复杂度为线性的O(N+C),其中C为桶内快排的时间复杂度。如果相对于同样的N,桶数量M越大,其效率越高,最好的时间复杂度达到O(N)。 当然桶排

    algorithm

    算法介绍复习算法排序算法小结排序算法平均时间复杂度最差时间...O($ n ^ 2 $) O(登录)不稳定堆排序O(登录) O(登录) O(1)不稳定计数排序O(n +米) O(n +米) O(米)稳定桶排序在) O(登录) 在)稳定

    leetcode下载-LeetCode:力码

    计数排序 稳定的排序 - 归并排序 链表的排序 - 归并排序 不足以加载到内存中 - 外部排序 时间复杂度 O(n) 最好使用 nlogn 甚至更小的 时间复杂度,因为这类方法在数据规模非常大时的效率非常高 ,迫不得已才使用n2...

    深入解析桶排序算法及Node.js上JavaScript的代码实现

    当要被排序的数据内的数值是均匀分配的时候,桶排序时间复杂度为Θ(n)。桶排序不同于快速排序,并不是比较排序,不受到时间复杂度 O(nlogn) 下限的影响。 桶排序按下面4步进行: (1)设置固定数量的空桶。 (2)把...

    leetcode2sumc-100-Days-of-codes:100天代码

    leetcode 2 和 c 100 天代码 使用 C++ 语言的 DSA 这个存储库包含我解决的所有编码问题。...在不同的文件夹中找到所有主题明智的问题:) 每个主题下从基础到困难级别的问题 ...计数排序 O(n+k) O(n+k) O(n+k) O

    leetcode用例构造-algorithms:TIL:不断练习算法

    计数排序 解决问题 力码 可依性 谷歌启动 日本央行 程序员 语 为编码面试选择正确语言的问题 您正在面试特定语言的工作吗? 你最好的语言是什么? 用语言解决算法问题有多容易? 不懂这门语言的人容易理解吗? 他们...

    leetcode下载-algorithm:算法数据结构学习

    计数排序 O(n+k) k是数据范围 是 否 桶排序 O(n) 是 否 基数排序 O(dn) d是维度 是 否 冒泡排序和插入排序的时间复杂度都是O(n2),都是原地排序算法,都是稳定的排序算法。 冒泡排序插和入排序不管怎么优化,元素...

    Python算法题源代码-LeetCode(力扣)-搜索旋转排序数组

    力扣热题Python源代码 ...你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。 示例 1: 输入:nums = [4,5,6,7,0,1,2], target = 0 输出:4 示例 2: 输入:nums = [4,5,6,7,0,1,2], target = 3 输出:-1

    InsightDataChallenge

    由于需要对单词进行排序,运行时间至少为O(n log(n)。为了保持单词的顺序,我使用Tree Map来记录单词和计数,其中word是key,count是值。Tree Map每次插入需要O(log(n))时间,N次插入总时间为O(N log(n)),空间...

Global site tag (gtag.js) - Google Analytics