内部排序(五)基数排序

基数排序是一种借助多关键字排序的思想对单逻辑关键字进行排序的方法。实现的过程不需要之前的所有排序所需要的记录关键字比较,移动等操作。

多关键字排序

多关键字排序通常有两种方法:

1、MSD(Most Significant Digit)法,最高为优先法

2、LSD(Least Significant Digit)法,最低位优先法

过程借助分配收集两种操作。

数组基数排序

过程演示

以LSD为例,假设原来有一串数值如下所示:

73, 22, 93, 43, 55, 14, 28, 65, 39, 81

首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:

0:

1: 81

2 :22

3 :73 93 43

4 :14

5 :55 65

6:

7:

8 :28

9 :39

第二步

接下来将这些桶子中的数值重新串接起来,成为以下的数列:

81, 22, 73, 93, 43, 14, 55, 65, 28, 39

接着再进行一次分配,这次是根据十位数来分配:

0:

1 :14

2 :22 28

3 :39

4 :43

5 :55

6 :65

7 :73

8 :81

9 :93

第三步

接下来将这些桶子中的数值重新串接起来,成为以下的数列:

14, 22, 28, 39, 43, 55, 65, 73, 81, 93

这时候整个数列已经排序完毕;如果排序的对象有三位数以上,则持续进行以上的动作直至最高位数为止。

LSD的基数排序适用于位数小的数列,如果位数多的话,使用MSD的效率会比较好。MSD的方式与LSD相反,是由高位数为基底开始进行分配,但在分配之后并不马上合并回一个数组中,而是在每个“桶子”中建立“子桶”,将每个桶子中的数值按照下一数位的值分配到“子桶”中。在进行完最低位数的分配后再合并回单一的数组中。

链式基数排序

过程演示

算法代码

#include <stdio.h>
#include <stdlib.h>

#define MAX_NUM_OF_KEY 8        //关键字项数的最大值
#define RADIX 10                //关键字基数,此时是十进制整数的基数
#define MAX_SPACE 10000
typedef int KeyType;            //定义关键字类型为int
typedef char InfoType;          //定义其他信息类型为char

typedef struct{
    KeyType keys[MAX_NUM_OF_KEY];//关键字
    InfoType otheritems;        //其它数据项
    int next;
}SLCell;                        //静态链表结点类型
typedef struct{
    SLCell r[MAX_SPACE];        //静态链表的可利用空间,r[0]为头结点
    int keynum;                 //记录的当前关键字个数
    int recnum;                 //静态链表的当前长度
}SLList;                        //静态链表类型
typedef int ArrType[RADIX];     //指针数组类型

//分配函数
//此函数按第i个关键字keys[i]建立RADIX个子表,使同一子表中记录的keys[i]相同
//静态链表L的r域中记录已按(keys[0],...,keys[i-1])有序
void Distribute(SLCell *r, int i, ArrType f,ArrType e){
    //f[0...RADIX-1]和e[0...RADIX-1]分别指向各子表中的第一个和最后一个记录。
    int j,p;

    for ( j = 0; j < RADIX; j++){//各子表初始化为空表
        f[j] = 0;
    }
    for (p= r[0].next;p;p=r[p].next){//遍历静态链表,通过修改指针将一个静态链表变成RADIX个子表(允许有空)
        j = ord(r[p].keys[i]);//ord将记录中第i个关键字映射到[0..RADIX-1],j代表第j个子表
        if (!f[j]){//f[j]为空,f[i]和p指向一致(指向此子表的第一个记录)
            f[j] = p;
        }else{//f[j]不为空(即子表已经有记录),则往后插(通过e[j]获取要插入位置的前一个位置,即插入前子表的最后位置)
            r[e[j]].next = p;
        }
        e[j] = p;//更改子表表尾指针
    }
}

//收集函数
//本函数按keys[i]自小到大地将f[0...RADIX-1]所指各子表依次链接成一个链表
//e[0...RADIX-1]为各子表的尾指针
void Collect(SLCell *r, int i, ArrType f, ArrType e){
    int j,t;

    for (j = 0; !f[j]; j=succ(j));//找第一个非空子表,succ为求后继函数。第一个非空子表的第一个结点的下标为j
    r[0].next = f[j];//r[0].next指向第一个非空子表中的第一个结点
    t = e[j];//t为上一个非空子表的尾指针

    while (j < RADIX){
        for (j = succ(j); (j < RADIX - 1) && !f[j]; j = succ(j));//找到下一个非空子表
        if (f[j]){                                               //链接两个非空子表
            r[t].next = f[j];                                    //上一个非空子表的尾指针连接到本非空子表的表头
            t = e[j];                                            //更新t
        }
    }
    r[t].next = 0;                                           //t指向最后一个非空子表中的最后一个结点
}

//此函数对L作基数排序,使得L成为按关键字自小到大的有序静态链表,L.r[0]为头结点
//L是采用静态链表表示的顺序表
void RadixSort(SLList *L){
    int i;

    //初始化静态链表
    for ( i = 0; i < L->recnum; i++){
        L->r[i].next = i + 1;
    }
    L->r[L->recnum].next = 0;
    //按LSD依次对个关键字进行分配和收集
    for ( i = 0; i <L->keynum; i++){
        Distribute(L->r, i, f, e);//第i趟分配
        Collect(L->r, i, f, e);   //第i趟收集
    }

}

评价

基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。

时间: 2024-11-08 01:51:39

内部排序(五)基数排序的相关文章

算法学习-02(希尔排序,计数排序,桶排序,基数排序)

希尔排序 # 希尔排序 # 希尔排序是对插入排序的升级改造 # 它的大致流程是 # 1.将长度为n的序列 分为d = n//2组 # 2.使每一组变的有序 # 3.将序列分为 d1 = d // 2 组 # 4.将每一组变的有序 # 5.直到最后 d 小于等于 0 def inster_sort_gap(li,gap): for i in range(gap,len(li)): tmp = li[i] j = i - gap while j >= 0 and tmp > li[j]: li[j

常见的五类排序算法图解和实现(多关键字排序:基数排序以及各个排序算法的总结)

基数排序思想 完全不同于以前的排序算法,可以说,基数排序也叫做多关键字排序,基数排序是一种借助“多关键字排序”的思想来实现“单关键字排序”的内部排序算法. 两种方式: 1.最高位优先,先按照最高位排成若干子序列,再对子序列按照次高位排序 2.最低位优先:不必分子序列,每次排序全体元素都参与,不比较,而是通过分配+收集的方式. 多关键字排序 例:将下表所示的学生成绩单按数学成绩的等级由高到低排序,数学成绩相同的学生再按英语成绩的高低等级排序.        第一个关键字是数学成绩,第二个关键字是英

内部排序-&gt;基数排序-&gt;链式基数排序

文字描述 基数排序是和前面各类排序方法完全不相同,前面几篇文章介绍的排序算法的实现主要是通过关键字间的比较和移动记录这两种操作,而实现基数排序不需要进行记录关键字间的比较.基数排序是一种借助多关键字排序的思想对单逻辑关键字进行排序的方法.先介绍下什么是多关键字排序,以引入链式基数排序算法. 先介绍什么是多关键字排序: 比如,对扑克牌进行排序,每张扑克牌有两个"关键字":花色(梅花<方块<红桃<黑桃)和面值(2<3<,-,A),且"花色"

排序---内部排序算法(快排、希尔排序、归并排序、基数排序、冒泡、选择排序)比较

1.内部排序的复杂度总结 1)时间复杂度 4种排序的平均时间复杂度是O(nlog2n),"快些以nlog2n的速度归队"(快排.希尔排序.归并.堆排序) 最坏情况下,快排的时间复杂度为O(n*n) 2)空间复杂度 O(log2n)快排 O(n)归并 O(rd)基数 其他都是O(1) 3)稳定性 不稳定的:"考研复习痛苦啊,情绪不稳定,快些选一堆好友来聊天吧"(快排.希尔.简单选择排序.堆排序) 其他都是稳定的. 4)一趟排序,保证一个关键字到达最终位置 交换类(起泡

常见的9种内部排序(C语言实现)

现在已经把常见的9种内部排序算法都用C语言实现了,为了方便自己和大家查看,就弄了这么一个类似于导航目录的东西. 一.冒泡排序 冒泡排序(C语言版) 二.选择排序 选择排序(C语言版) 三.直接插入排序 直接插入排序(C语言版) 四.希尔排序 希尔排序(C语言版) 五.归并排序 归并排序(C语言版) 六.基数排序 基数排序(C语言版) 七.快速排序 快速排序(C语言版) 八.计数排序 计数排序(C语言版) 九.堆排序 堆排序(C语言版) 介绍完这九个常用的排序算法,怎么能没有一个比较呢?下面是我对

10-11-基数排序-内部排序-第10章-《数据结构》课本源码-严蔚敏吴伟民版

课本源码部分 第10章  内部排序 - 基数排序 ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑        本源码引入的文件  链接? Status.h.Scanf.c        相关测试数据下载  链接? 数据

几种内部排序-分类-复杂性-稳定性

1. 简述 本文主要说明一些常用的内部排序算法的分类.复杂性和稳定性.主要基于现在的理解和学习,详细准确的复杂度可以参见维基百科等比较权威的网站,对于一些算法的不同实现,复杂度也不同,这里给出的复杂度都是相对较好的算法的复杂度. 2. 分类    3. 复杂性和稳定性 冒泡排序:在已经有序的情况,取得O(N)的复杂度.    快速排序:每次递归都是N的复杂度,递归次数根据序列有关系,当已经有序的情况下,递归N次,时间复杂度为O(N*LogN)    插入排序:在已经有序的情况,取得O(N)的复杂

内部排序-第10章-《数据结构题集》习题解析-严蔚敏吴伟民版

//**留坑待填**// 一.基础知识题 10.1?以关键码序列(503,087,512,061,908,170,897,275,653,426)为例,手工执行以下排序算法,写出每一趟排序结束时的关键码状态: (1)直接插入排序:                            (2)希尔排序(增量d[1]=5): (3)快速排序:                                  (4)堆排序: (5)归并排序:                              

10-12-顺序表地址排序-内部排序-第10章-《数据结构》课本源码-严蔚敏吴伟民版

课本源码部分 第10章  内部排序 - 顺序表地址排序 ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑        本源码引入的文件  链接? SequenceListType.c        相关测试数据下载  链

基于比较的内部排序总结

★ 基于“比较”操作的内部排序性能大PK 我们首先总结一下<排序结构专题1-4>中的十种方法的性能((N个关键字的待排序列)): 排序方法        平均时间   最坏时间    辅助存储空间   稳定性   直接插入排序 O(N^2)   O(N^2)    O(1)                √      折半插入排序 O(N^2) O(N^2)  O(1)      √ 希尔排序  O(N*logN) O(N*logN) O(1)        × 起泡排序        O(N