Chinaunix首页 | 论坛 | 博客
  • 博客访问: 148743
  • 博文数量: 54
  • 博客积分: 1732
  • 博客等级: 上尉
  • 技术积分: 520
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-23 23:29
文章分类

全部博文(54)

文章存档

2011年(3)

2010年(26)

2009年(25)

分类: C/C++

2009-11-13 00:26:07

计数排序假设输入数据均为正整数,且范围已知(即最大值已知),其运行时间规模为O(n)。

原理简述:考虑输入集中数据互不相等的情况,(1)新建一个临时数组,其维度(元素数量)与输入集最大值一致,(2)对输入集中的数据进行扫描,以输入集中的元素值为索引,将临时数组的元素置1,(3)扫描临时数组,若数组某元素为1,则将其索引值填入目标数组
当输入集中的数据有重复时需要做少许修改
计数排序不是原地排序,但是对于重复的元素,排序完成后它能保持其相对秩序

注:本文主要考虑输入集中的数据有重复的情况,若已知输入集中数据互不相等,可以将临时数组改为位向量,具体解法见《编程珠玑I》第一章(这个我没有动手实践过)

第一次在这个上面插代码,注释前混用了tab和空格

根据上面分析的前提条件,将算法的接口设计为
count_Sort(srcBegin, srcEnd, dest, upperBound)
因为目标迭代器使用了operator[],所以其必须是随机迭代器而不能使用std::back_insertor,这一点似乎很不科学



/**********************************************************************
 * 用模板实现计数排序
 * 输入: 前向迭代器begin, end,
 *      指示目标区间起始地址的迭代器(必须是随机迭代器)
 *      待排序集合的最大值
 *
 * 执行结果:将区间[begin, end)内的值按照"<"排序后置于以dest开始的区间中
 * 注意: 输入集合内的值必须属于区间[0, upBound);
 * 函数不检查参数合法性
 *
 * author: compass
 * date: 2009年 11月 12日 星期四 19:44:58 CST
 * Email: xjtu.ym@gmail.com
 **********************************************************************/


#ifndef __COUNTSORT_H__
#define __COUNTSORT_H__

#include <iterator>

template<typename IterSrc, typename IterDest>
void count_Sort(const IterSrc &begin, const IterSrc &end, /*输入区间*/
        IterDest dest,                                    /* 目标区间 */

        /* 下面为待排序集合的最大值 */
        const typename std::iterator_traits<IterSrc>::value_type upBound)  
{
    typedef typename std::iterator_traits<IterSrc>::value_type _ValueType;

    if(begin == end)                /*处理空区间 */
        return;

    _ValueType countArray[upBound];

    /* 初始化数组 */
    std::fill( countArray, countArray+upBound, 0);

    /* 统计每个*iter出现的次数 */
    for(IterSrc iter=begin; iter != end; ++iter)
        countArray[*iter] += 1;

    /* countArray[i]存储小于或等于i的元素个数 */
    for(size_t i=1; i!=upBound; ++i)
        countArray[i] +=countArray[i-1];
    
    for(IterSrc iter=begin; iter != end; ++iter)
    {
        /* 考虑iter指向最小的元素且该元素不重复,则
         * countArray[*iter]为1,
         * 而我们需要使dest[0]为最小的元素,而不是dest[1],
         * 其他同理
         */

        dest[ countArray[(*iter)]-1 ] = *iter;
        countArray[(*iter)] -= 1;
    }

    return;
}

#endif        /*countSort.h */


上面的临时数组countArray不能在定义时初始化,因为编译时其大小是不可知的

在我的第一个版本的最后一个
for循环中,dest的索引表达式没有减1,导致内存非法方位,而我又不习惯用gdb调试,于是添加std::cout,然后该死的是添加的调试代码中竟然犯了一个相当低级的错误:数组索引从1开始,花了两个小时才把调试代码中的这个bug揪出来,当然顺便把原本存在的问题也解决了。
不知道上面代码还有没有其他的错误,欢迎批评指正(包括编程风格,stl使用等)

我的测试代码:

/**********************************************************************
 * 本程序用于对整数排序,调用计数排序
 * 从stdin读入数据,将排序好的数据输出到stdout,每行输出20个数
 * 读入数据为区间[0, 100)范围内的整数
 * date: 2009年 10月 11日 星期日 16:24:48 CST
 * 作者:compass
 * Email: xjtu.ym@gmail.com
 **********************************************************************/

#include "countSort.h"
#include <iomanip>
#include <iostream>
#include <cstdlib> //for EXIT_SUCCESS
#include <iterator> //for iterators in fuction copy()
#include <algorithm> //for copy()
#include <vector>

int main(int argc, char* arg[])
{
    std::vector<int> iVecSrc;

    std::istream_iterator<int> eof;
    std::istream_iterator<int> inIter(std::cin);
    copy(inIter, eof, // the source
            std::back_inserter(iVecSrc) ); // the destination


    //保证目标容器有足够的容积
    //因为count_Sort()中对目标迭代器使用的赋值而不是插入,
    //所以这里不能用 iVecDest.reserve();
    std::vector<int> iVecDest(iVecSrc);


    //调用排序算法
    count_Sort(iVecSrc.begin(), iVecSrc.end(),
         iVecDest.begin(), //目标区间迭代器必须是随机迭代器

          100);
       
    //输出
    int n=1;
    for(std::vector<int>::const_iterator iter=iVecDest.begin();
            iter != iVecDest.end();
            ++iter, ++n)
    {
        std::cout.setf(std::ios::right);
        std::cout << std::setw(10) << *iter << " ";
        if(10 == n)
        {
            std::cout<<std::endl;
            n=0;
        }
    }

    std::cout << std::endl;
    return EXIT_SUCCESS;
}


参考资料:《算法导论》第8章
        
阅读(730) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~