非比较排序算法之计数排序及其实现

  计数排序是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;  
    }  
}  

 

  

posted @ 2013-03-19 11:30  busyfruit  阅读(319)  评论(0编辑  收藏  举报