数据结构基础(15) --基数排序

基数排序是一种借助“多关键字排序”的思想来实现“单关键字排序”的内部排序算法。

实现多关键字排序通常有两种作法:

 最低位优先法(LSD)

先对K[0]{基数的最低位}进行排序,并按 K(0) 的不同值将记录序列分成若干子序列之后,分别对 K[1] 进行排序,..., K[d-1]依次类推,直至最后对最次位关键字排序完成为止。

  最高位优先法(MSD)

先对 K[d-1]{基数的最高位}进行排序,然后对 K[d-2]进行排序,依次类推,直至对最主位关键字 K[0] 排序完成为止。

百度百科对基数排序做了如下介绍:

基数排序(radix sort)是属于“分配式排序”(distribution sort),基数排序法又称“桶子法”(bucket sort),顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,在某些时候,基数排序法的效率高于其它的稳定性排序法。

链式基数排序基本步骤如下

1.将待排序记录以数组存储[或者以指针相链,构成一个链表]

2.”分配”时,按当前”关键字位”所取值,将记录分配到不同的”链表/链队列”(即不同的桶或堆中)中,每条链表中记录的”关键字位”相同;

3.”收集”时,按当前关键字位取值从小到大(即将这n条链表(n的大小为基数的大小)按照编号, 依次将其中所有的元素取出)将各链表中的元素取出放入到原先的数组或链表中;

4.对每个关键字位均重复 2) 和 3) 两步n次。

如采用LSD对{179, 208, 306, 93, 859, 984, 55, 9, 271, 33}(构成一个链表或者是数组)进行基数排序:

[第一步:按个位排]

[第二步:按十位排]

[第三步:按百位排]

代码实现(以LSD为例):

//寻找数组中最大数字的位数
template <typename Type>
unsigned int maxBits(Type *begin, Type *end)
{
    unsigned int bits = 1;
    //standard作为基准, 如果array中的元素
    //大于standard, 则bits+1
    int standard = 10;

    for (Type *current = begin; current != end; ++current)
    {
        while (*current >= standard)
        {
            standard *= 10;
            ++ bits;
        }
    }

    return bits;
}
/**说明:
  begin:数组起始
  end:数组结尾
  radix:基数

*/
#define DEBUG
template <typename Type>
void radixSort(Type *begin, Type *end, int radix)
{
    //找到数组中最大数字的位数
    int bits = maxBits(begin, end);

    //基数为radix, 则需要radix个链表
    std::list<Type> lists[radix];

    // 需要循环bits次
    for (int d = 0, factor = 1; d < bits; ++d, factor*=10)
    {
        //分配...
        for (Type *current = begin; current != end; ++current)
        {
            //取出相应位置上的数 (比如个位是1)
            int number = ((*current)/factor)%10;
            //则需要将之放到(分配到)标号为1的链表中
            lists[number].push_back(*current);
        }

        //收集...
        Type *current = begin;
        //对radix个链表中的元素进行收集
        for (int i = 0; i < radix; ++i)
        {
            while (!lists[i].empty())
            {
                *current = lists[i].front();

                ++ current;
                lists[i].pop_front();
            }
        }
#ifdef DEBUG
        //打印排序的中间结果
        for (current = begin; current != end; ++ current)
        {
            cout << *current << ‘ ‘;
        }
        cout << endl;
#endif // DEBUG
    }
}

template <typename Type>
void radixSort(Type *array, int arraySize, int radix)
{
    return radixSort(array, array+arraySize, radix);
}

时间复杂度分析:

设待排序列为n个记录,d个关键码,关键码的取值范围为radix,则进行链式基数排序的时间复杂度为O(d(n+radix)),其中,一趟分配时间复杂度为O(n),一趟收集时间复杂度为O(radix),共进行d趟分配和收集.

附-测试代码:

int main()
{
    int array[10];
    for (int i = 0; i < 10; ++i)
    {
        array[i] = rand()%1000;
    }
    for (int i = 0; i < 10; ++i)
    {
        cout << array[i] << ‘ ‘;
    }
    cout << endl;

    radixSort(array, 10, 10);

    for (int i = 0; i < 10; ++i)
    {
        cout << array[i] << ‘ ‘;
    }
    cout << endl;

    return 0;
}

时间: 2024-08-06 11:58:32

数据结构基础(15) --基数排序的相关文章

数据结构基础温故-6.查找(上):基本查找与树表查找

只要你打开电脑,就会涉及到查找技术.如炒股软件中查股票信息.硬盘文件中找照片.在光盘中搜DVD,甚至玩游戏时在内存中查找攻击力.魅力值等数据修改用来作弊等,都要涉及到查找.当然,在互联网上查找信息就更加是家常便饭.查找是计算机应用中最常用的操作之一,也是许多程序中最耗时的一部分,查找方法的优劣对于系统的运行效率影响极大.因此,本篇讨论一些查找方法. 一.顺序查找 1.1 基本思想 顺序查找(Sequential Search)又叫线性查找,是最基本的查找技术,它的查找过程是:从表中第一个(或最后

数据结构和算法 (二)数据结构基础、线性表、栈和队列、数组和字符串

Java面试宝典之数据结构基础 —— 线性表篇 一.数据结构概念 用我的理解,数据结构包含数据和结构,通俗一点就是将数据按照一定的结构组合起来,不同的组合方式会有不同的效率,使用不同的场景,如此而已.比 如我们最常用的数组,就是一种数据结构,有独特的承载数据的方式,按顺序排列,其特点就是你可以根据下标快速查找元素,但是因为在数组中插入和删除元素会 有其它元素较大幅度的便宜,所以会带来较多的消耗,所以因为这种特点,使得数组适合:查询比较频繁,增.删比较少的情况,这就是数据结构的概念.数据结构 包括

数据结构基础总结

数据的逻辑结构 数据的存储结构 单链表是递归结构 迭代是指从当前元素获得集合中的后继元素. 迭代功能由Tterable可迭代接口和Tterator迭代器接口实现. 栈和队列 是两种特殊的线性表,特殊之处在于插入和删除操作的位置受到限制. 栈:插入和删除只允许在线性表的一端进行,后进先出. 队列:插入和删除分别在线性表的两端进行,先进先出. 数组: 1.数组是随机存取结构,这是数组最大的优点. 2.数组一旦占用一片存储空间,这片存储空间的地址和长度就确定的,不能更改,因此数组只能进行赋值.取值两种

中国大学MOOC-陈越、何钦铭-数据结构基础习题集 03-1. 二分法求多项式单根

03-1. 二分法求多项式单根(20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 杨起帆(浙江大学城市学院) 二分法求函数根的原理为:如果连续函数f(x)在区间[a, b]的两个端点取值异号,即f(a)f(b)<0,则它在这个区间内至少存在1个根r,即f(r)=0. 二分法的步骤为: 检查区间长度,如果小于给定阈值,则停止,输出区间中点(a+b)/2:否则 如果f(a)f(b)<0,则计算中点的值f((a+b)/2): 如

翻译:程序员数据结构基础:选择正确的数据结构

本文转载自GameDev.net,仅供学习交流.因为刚刚开始学习翻译,难免有些疏漏,如果有哪些地方翻译的不正确,请不吝告知,万分感谢. 原文链接:http://www.gamedev.net/page/resources/_/technical/general-programming/data-structures-for-pre-college-programmers-choosing-the-right-structure-r2991 网络上的许多初学者还是学生.通常初学者通过在网上看教程,

算法与数据结构基础10:C++实现——拓扑排序

一 定义 拓扑排序是对有向无环图(Directed Acyclic Graph简称DAG)顶点的一种排序, 它使得如果存在一条从顶点A到顶点B的路径,那么在排序中B出现在A的后面. 二 先决条件 能够进行拓扑排序图有两个先决条件:有向.无环,即有向无环图. 三 偏序全序 连通图:任意两点之间都存在至少一条边 偏序:非连通图(有向无环图满足偏序关系) 全序:单连通图 四 结果唯一性 对于仅满足偏序关系的有向无环图中,因为有两个点之间的关系是不确定的,所以导致排序的结果是不唯一的. 满足全序关系的有

OC基础(15)

@property参数 @Property练习 @class 循环retian *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } a { color: #4183C4; } a.absent { color: #cc0000; } a.anchor { display: block; padding-left: 30px; margin-left: -3

[.net 面向对象编程基础] (15) 抽象类

[.net 面向对象编程基础] (15) 抽象类 前面我们已经使用到了虚方法(使用 Virtual修饰符)和抽象类及抽象方法(使用abstract修饰符)我们在多态一节中说到要实现类成员的重写必须定义为一个虚方法或抽象方法.这节单独把抽象类提出来,是因为抽象是.net实现面向对象编程重要的重要思想,定义抽象类就象一个模板一个提纲,它可以理解为中央的指导思想,我们通过派生类去具体实现它.由此可见,抽象类本身没有任何作用,也不能被实例化,因为他本身就不具有具体的事物.比如上节的动物类的例 子,我们实

算法与数据结构基础11:C++实现——二拆搜索树节点删除

基于我的另一篇文章<算法与数据结构基础4:C++二叉树实现及遍历方法大全> ,二叉树的结构用的这篇文章里的. 二查找叉树的删除可以细分为三种情况: 1 被删除的是叶子节点,直接删除: 2 被删除只有一个子节点,指针下移: 3 有两个子节点,为了不破坏树的结构,需要找出一个节点来替换当前节点. 根据二叉树的特点,当前节点大于所有左子树,小于所有右子树, 可以用左子树中最大的节点,或者右子树最小的节点来替换当前节点,然后删除替换节点. // BSTree.h #include <cstdio