计数排序(Counting sort)

大家都知道的是,基于比较的排序算法的时间复杂度的下界是 O(n log(n))。这一结论是可以证明的,所以在基于比较的算法中是找不到时间复杂度为 O(n)的算法的。这时候,非基于比较的算法,如计数排序、基数排序和桶排序,是可以突破这个下界的。但是,非基于比较的排序的使用限制却是较多的,如计数排序仅能对较小整数进行排序,且要求排序的数据的规模不能过大;基数排序可以对长整数进行排序,但是不适用于浮点数;桶排序可以对浮点数进行排序。

计数排序(Counting sort)是一种稳定的排序算法。计数排序是最简单的特例,由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存,适用性不高。例如:计数排序是用来排序0到100之间的数字的最好的算法,但是它不适合按字母顺序排序人名。但是,计数排序可以用在基数排序中的算法来排序数据范围很大的数组。当输入的元素是 n 个 0 到 k 之间的整数时,它的运行时间是 Θ(n + k)。

    假定输入是个数组A【1...n】, length【A】=n。 另外还需要一个存放排序结果的数组B【1...n】,以及提供临时存储区的C【0...k】(k是所有元素中最大的一个)。算法伪代码

1337947779_3381

算法的步骤如下

  1. 找出待排序的数组中最大和最小的元素
  2. 统计数组中每个值为t的元素出现的次数,存入数组C的第t
  3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
  4. 反向填充目标数组:将每个元素t放在新数组的第C(t)项,每放一个元素就将C(t)减去1

算法实现:

#include <iostream>
#include <ctime>
#include <algorithm>
#define NUM_MAX    10
using namespace std;
void myswap(int &a, int &b);
void CountSort(int *p, int n);
int main(void)
{
    int arr[NUM_MAX];
    srand((unsigned)time(NULL));
    for(int i = 0; i < NUM_MAX; i++)
    {
        arr[i] = rand()%100 + 1;
        cout<<arr[i]<<" ";
    }
    cout <<endl;
    CountSort(arr, NUM_MAX);
    
    for(int i = 0; i < NUM_MAX; i++)
    {
        cout<<arr[i]<<" ";
    }
    cout<<endl;
    return 0;
}
void myswap(int &a, int &b)
{
/*
    int tmp = a;
    a = b;
    b = tmp;*/
//以下这两种方法时,要考虑不能相等才进行交换,否则出错    
/*
    f(a != b)
    {
        a = a + b;
        b = a - b;
        a = a - b;
        
    }
    
    */
    if(a != b)
    {
        a = a ^ b;
        b = a ^ b;
        a = a ^ b;
        
    }    
}
void CountSort(int *p, int n)
{
    int max_val = *max_element(p, p+n);//求数组最大值
    int *c = new int[max_val+1]; // 开辟一个临时数组
    int *tmp = new int[n];// 开辟一个保存结果的临时数组 
    for(int i = 0; i <= max_val; i++)
        c[i] = 0;
        // 使count[i]等于等于i的元素的个数
    for(int i = 0; i < n; i++)
        c[ p[i] ]++;
    for(int j = 1; j <= max_val; j++)
        c[j] += c[j-1];
    
    for(int i = 0; i < n; i++)
    {
        --c[ p[i]];// 因为数组下标从0开始,所以这个放在前面
        // 这个比较绕, count[input[i]-1] 就代表小于等于元素input[i]的元素个数,就是input[i]在result的位置
        tmp[ c[ p[i]]] = p[i];
    }
    copy(tmp, tmp+n, p);// 调用copy函数把结果存回原数组 
    delete [] c;
    delete [] tmp;
}

结果:

QQ截图20140228215724

这个实现对书上给的伪代码稍微改了一点, 如果计数排序每次要自己另外开一个数组保存结果才能用,感觉肯定很不爽。 所以在计数排序里面,把结果在拷贝到原来的数组。这样用的时候不用自己开数组,方便多了

我这里有一个更简单的" 计数排序 ",也可以实现排序。但是这个却又不太像计数排序。到底这个算不算是计数排序呢? 这个问题也困扰了我很长时间,终于在某一天让我给发现了偷笑。 这个再后面再讲。 先把这个取名为“计数排序特殊版

代码:

void CountSort(int *p, int n)
{
    int max_val = *max_element(p, p+n);//求数组最大值
    int *c = new int[max_val+1]; // 开辟一个临时数组

    for(int i = 0; i <= max_val; i++)
        c[i] = 0;
        // 使count[i]等于等于i的元素的个数
    for(int i = 0; i < n; i++)
        c[ p[i] ]++;

    int j = 0;
    for (i = 0; i <= max_val; i++) 
    {
        while (c[i]-- > 0) 
        {
            ar[j++] = i;
        }
    }


    delete [] c;

}

“计数排序特殊版”:其实那是一个“特殊的桶排序”,一共有k个桶,然后把每个元素都放到对应的那个桶中。和一般的桶排序不同,这里每个桶放的都是相同的元素,所以最后不需要再用另外一个排序算法给每个桶再排序,直接把所有的桶合并在一起就是最终的排序了!

posted @ 2014-02-28 22:07  mickole  阅读(404)  评论(0)    收藏  举报