经典算法之计数排序

一 引言

计数排序假设n个输入元素中的每一个都是介于0-k的整数,此处k为某个整数。当k等于O(n)时,计数排序的运行时间为Θ(n)。


二 基本思想

计数排序的基本思想就是对每一个输入元素x,确定小于x的元素个数。因此我们就可以直接把x放到最终输出数组中的相应位置上。

例如:

如果有17个元素小于x,则x就位于第18个输出的位置上。当然有几个元素相同时,这个方法就略微改进一下,因为不能将它们放在同一个位置上。


三 具体实现

假定输入数组为A[1..n],他们的值均位于0~k之间,输出排序之后的数组为B[1..n],以及临时存储数组C[0..k]。计数排序的伪代码如下:

上图显示出了计数排序的运行过程。

在第1~2行中的for循环初始化操作之后,在第3~4行检查输入的每一个元素。如果输入元素的值为i,即增加C[i]的值。C[i]记录了值为i的元素个数。

于是,在第4行之后C[i]中存放了等于i的元素的个数。在第6~7行中,通过数组C中记录计数和,可以确定对每一个i=0,1,2.....,k,有多少输入元素是小于等于i的。

最后,在第9~11行中的for循环部分,把每一个元素A[j]放在输出数组B中与其相应的最终位置上。如果每一个元素都不相同,则当第一次执行第9行时,对每一个A[j],值C[A[j]]即为A[j]在输出数组上的最终位置上,因为共有C[A[j]]个元素小于等于A[j]。

可是由于各个元素可能不一定是不同的,因此,每当将一个值A[j]放入数组B中时,都要减小C[A[j]]额值。这会使得下一个值等于A[j]的输入元素直接进入输出数组B中A[j]的前一个位置。


四 代码

/*********************************
*   日期:2014-04-26
*   作者:SJF0115
*   题目: 计数排序
**********************************/
#include <iostream>
#include <stdio.h>
using namespace std;

void COUNTINGSORT(int *A,int *B,int len,int k){
    if(A == NULL || k <= 0 || len <= 0){
        return;
    }
    int C[k+1],i;
    //初始化
    for(i = 0;i <= k;i++){
        C[i] = 0;
    }
    //统计值为A[i]的个数,C[i]是等于i的元素个数
    for(i = 0;i < len;i++){
        C[A[i]] ++;
    }
    //确定值A[i]在最终输出数组B中位置,C[i]是小于等于i的元素个数
    for(i = 1;i <= k;i++){
        C[i] += C[i-1];
    }
    //输出到数组B中
    for(i = len-1;i >= 0;i--){
        //index元素A[i]在数组B中的下标
        int index = C[A[i]];
        B[index] = A[i];
        //如果有相同值元素的情况
        C[A[i]] --;
    }
    //B下标从1开始
}

int main(){
    int A[8] = {2,5,3,0,2,3,0,3};
    int B[9];
    COUNTINGSORT(A,B,8,5);
    for(int i = 1;i <= 8;i++){
        printf("%d\n",B[i]);
    }
    return 0;
}

五 时间复杂度分析

时间复杂度是多少呢?

第1~2行for循环所花时间为Θ(k)。

第3~4行for循环所花时间为Θ(n)。

第6~7行for循环所花时间为Θ(k)。

第9~11行for循环所花时间为Θ(n)。

这样总的时间就是Θ(k+n)。在实践中,档k = O(n)时,我们常采用计数排序,这时运行时间为Θ(n)。


六 特点

1.  提前必须是已知待排序的关键字为整型且范围已知。

2.  时间复杂度为O(n+k),不是基于比较的排序算法,因此效率非常之高。

3.  稳定性好,这个是计数排序非常重要的特性,可以用在后面介绍的基数排序中。

4.  但需要一些辅助数组,如C[0..k],因此待排序的关键字范围0~k不宜过大。

时间: 2024-12-11 05:53:06

经典算法之计数排序的相关文章

经典算法(8) MoreWindows白话经典算法之七大排序总结篇

在我的博客对冒泡排序,直接插入排序,直接选择排序,希尔排序,归并排序,快速排序和堆排序这七种 常用的排序方法进行了详细的讲解,并做成了电子书以供大家下载.下载地址为: http://download.csdn.net/detail/morewindows/4443208. 有网友提议到 这本<MoreWindows白话经典算法之七大排序>电子书讲解细致用来平时学习是非常好的,但是页数有22页, 不太合适做面试前的复习资料.因此在这里将这七种常用的排序方法进行下总结,以便大家更好的复习这些 经典

Javascript排序算法之计数排序的实例

 计数排序是一种高效的线性排序,它通过计算一个集合中元素楚翔的次数来确定集合如何排列,计数排序不需要进行数据的比较,所有他的运行效率前面介绍的都高 计数排序(Counting sort)是一种稳定的排序算法.计数排序使用一个额外的数组Count_arr,其中第i个元素是待排序数组Arr中值等于i的元素的个数.然后根据数组Count_arr来将Arr中的元素排到正确的位置. 分为四个步骤: 1.找出待排序的数组中最大和最小的元素 2.统计数组中每个值为i的元素出现的次数,存入数组Count_arr

Javascript排序算法之计数排序的实例_javascript技巧

计数排序(Counting sort)是一种稳定的排序算法.计数排序使用一个额外的数组Count_arr,其中第i个元素是待排序数组Arr中值等于i的元素的个数.然后根据数组Count_arr来将Arr中的元素排到正确的位置.分为四个步骤:1.找出待排序的数组中最大和最小的元素2.统计数组中每个值为i的元素出现的次数,存入数组Count_arr的第i项3.对所有的计数累加(从Count_arr中的第一个元素开始,每一项和前一项相加)4.反向遍历原数组:将每个元素i放在新数组的第Count_arr

经典算法(3) 希尔排序的实现

希尔排序的实质就是分组插入排序,该方法又称缩小增量排序,因DL.Shell于1959年提出而得名. 该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个"增量"的元素组成 的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小) 时,再对全体元素进行一次直接插入排序.因为直接插入排序在元素基本有序的情况下(接近最好情况), 效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高. 以n=10的一个数组49, 38, 65,

经典算法:选择排序、插入排序和气泡排序的实现

将要排序的对象分作两部分,一个是一排序的,一个是未排序的,从后面未排序部分选择一个最小 值,并放入前面已排序部分的最后一个.例如: 排序前:70 80 31 37 10 1 48 60 33 80 [1] 80 31 37 10 70 48 60 33 80 选出最小值1 [1 10] 31 37 80 70 48 60 33 80 选出最小值10 [1 10 31] 37 80 70 48 60 33 80 选出最小值31 [1 10 31 33] 80 70 48 60 37 80 ....

经典的7种排序算法 原理C++实现

经典的7种排序算法 原理C++实现 排序是编程过程中经常遇到的操作,它在很大程度上影响了程序的执行效率. 7种常见的排序算法大致可以分为两类:第一类是低级排序算法,有选择排序.冒泡排序.插入排序:第二类是高级排序算法,有堆排序.排序树.归并排序.快速排序. 一.低级排序算法 1. 选择排序 排序过程:给定一个数值集合,循环遍历集合,每次遍历从集合中选择出最小或最大的放入集合的开头或结尾的位置,下次循环从剩余的元素集合中遍历找出最小的并如上操作,最后直至所有原集合元素都遍历完毕,排序结束. 实现代

经典白话算法之桶排序

最快最简单的排序--桶排序 在我们生活的这个世界中到处都是被排序过的.站队的时候会按照身高排序,考试的名次需要按照分数排序,网上购物的时候会按照价格排序,电子邮箱中的邮件按照时间排序--总之很多东西都需要排序,可以说排序是无处不在.现在我们举个具体的例子来介绍一下排序算法. 首先出场的我们的主人公小哼,上面这个可爱的娃就是啦.期末考试完了老师要将同学们的分数按照从高到低排序.小哼的班上只有5个同学,这5个同学分别考了5分.3分.5分.2分和8分,哎考的真是惨不忍睹(满分是10分).接下来将分数进

经典算法题每日演练——第二十三题 鸡尾酒排序

原文:经典算法题每日演练--第二十三题 鸡尾酒排序     这篇我们继续扯淡一下鸡尾酒排序,为了知道为啥取名为鸡尾酒,特意看了下百科,见框框的话,也只能勉强这么说了.   要是文艺点的话,可以说是搅拌排序,通俗易懂点的话,就叫"双向冒泡排序",我想作为码农的话,不可能不知道冒泡排序, 冒泡是一个单向的从小到大或者从大到小的交换排序,而鸡尾酒排序是双向的,从一端进行从小到大排序,从另一端进行从大 到小排序. 从图中可以看到,第一次正向比较,我们找到了最大值9.             

经典算法题每日演练——第二十二题 奇偶排序

原文:经典算法题每日演练--第二十二题 奇偶排序   这个专题因为各种原因好久没有继续下去了,MM吧...你懂的,嘿嘿,不过还得继续写下去,好长时间不写,有些东西有点生疏了, 这篇就从简单一点的一个"奇偶排序"说起吧,不过这个排序还是蛮有意思的,严格来说复杂度是O(N2),不过在多核的情况下,可以做到 N2 /(m/2)的效率,这里的m就是待排序的个数,当m=100,复杂度为N2 /50,还行把,比冒泡要好点,因为重点是解决问题的奇思妙想.     下面我们看看这个算法是怎么描述的,既