文字描述

  基数排序是和前面各类排序方法完全不相同,前面几篇文章介绍的排序算法的实现主要是通过关键字间的比较和移动记录这两种操作,而实现基数排序不需要进行记录关键字间的比较。基数排序是一种借助多关键字排序的思想对单逻辑关键字进行排序的方法。先介绍下什么是多关键字排序,以引入链式基数排序算法。

  先介绍什么是多关键字排序:

  比如,对扑克牌进行排序,每张扑克牌有两个“关键字”:花色(梅花<方块<红桃<黑桃)和面值(2<3<,…,A),且“花色”的地位高于”面值”, 那么对扑克牌排序有两种方法:

  方法1:先按不同“花色”分成有次序的4堆,每一堆的”花色”相同; 然后分别对每一堆内部按”面值”大小整理有序。这种先对主键字字进行排序,再对次关键字排序的方法叫最高位优先法(简称MSD: Most Significant first)

  方法2:先按不同”面值”分成13堆,然后将13堆自小到大叠在一起(“3”在”2”之上,”4”在”3”之上,…,最上面的是4张”A”),然后将这幅牌整个颠倒过来再重新按不同花色分成4堆,最后将这4堆按自小到的次序合在一起(梅花在最下面,黑桃在最上面)。这种先对次键字字进行排序,再对主关键字排序的方法叫最低位优先法(简称LSD: Least Significant first)

  采用第二种方法LSD法对多关键字进行排序时,也可以不采用之前介绍的各种通过关键字间的比较来实现排序的方法,而是通过若干次“分配”和“收集”来实现排序。

  关于链式基数排序的介绍:

  采用多关键字排序中的LSD方法,先对低优先级关键字排序,再按照高点的优先级关键字排序,不过基数排序在排序过程中不需要经过关键字的比较,而是借助“分配”和“收集”两种操作对单逻辑关键字进行排序的一种内部排序方法。

  比如,若关键字是十进制表示的数字,且范围在[0,999]内,则可以把每一个十进制数字看成由三个关键字组成(K0, K1, K2),其中K0是百位数,K1是十位数,K2是个位数。基RADIX的取值为10; 按LSD进行排序,从最低位关键字起,按关键字的不同值将序列中记录“分配”到RADIX个队列中后再“收集”之,如此重复d次。按这种方法实现的排序称之为基数排序,以链表作存储结构的基数排序叫链式基数排序。

示意图

算法分析

  对n个记录(假设每个记录含d个关键字,每个关键字的取值范围为rd个值)进行链式基数排序的时间复杂度为d*(n+rd),其中每一躺分配的时间复杂度为n,每一躺收集的时间复杂度为rd,整个排序需进行d躺分配和收集。

  所需辅助空间为2*rd个队列指针,由于采用链表作存储结构,相对于其他采用顺序存储结构的排序方法而言,还增加了n个指针域的空间。

  链式基数排序是稳定的排序。

代码实现

 #include <stdio.h>
#include <stdlib.h>
#include <string.h> #define DEBUG #define EQ(a, b) ((a) == (b))
#define LT(a, b) ((a) < (b))
#define LQ(a, b) ((a) <= (b)) //关键字项数的最大个数
#define MAX_NUM_OF_KEY 8
//关键字基数,此时是十进制整数的基数就是10
#define RADIX 10
//静态链表的最大长度
#define MAX_SPACE 10000 //定义结点中的关键字类型为int
typedef int KeyType;
//定义结点中除关键字外的附件信息为char
typedef char InfoType; //静态链表的结点类型
typedef struct{
//关键字
KeyType keys[MAX_NUM_OF_KEY];
//除关键字外的其他数据项
InfoType otheritems;
int next;
}SLCell; //静态链表类型
typedef struct{
//静态链表的可利用空间,r[0]为头结点
SLCell r[MAX_SPACE];
//每个记录的关键字个数
int keynum;
//静态链表的当前长度
int recnum;
}SLList; //指针数组类型
typedef int ArrType[RADIX]; void PrintSList(SLList L)
{
int i = ;
printf("下标值 ");
for(i=; i<=L.recnum; i++){
printf(" %-6d", i);
}
printf("\n关键字 ");
for(i=; i<=L.recnum; i++){
printf(" %-1d%-1d%-1d,%-2c", L.r[i].keys[], L.r[i].keys[], L.r[i].keys[], L.r[i].otheritems);
}
// printf("\n其他值 ");
// for(i=0; i<=L.recnum; i++){
// printf(" %-5c", L.r[i].otheritems);
// }
printf("\n下一项 ");
for(i=; i<=L.recnum; i++){
printf(" %-6d", L.r[i].next);
}
printf("\n");
return;
} void PrintArr(ArrType arr, int size)
{
int i = ;
for(i=; i<size; i++){
printf("[%d]%-2d ", i, arr[i]);
}
printf("\n");
} /*
*静态链表L的r域中记录已按(key[0],...,key[i-1])有序
*本算法按第i个关键字keys[i]建立RADIX个子表,使同一子表中记录的keys[i]相同。
*f[0,...,RADIX-1]和e[0,...,RADIX-1]分别指向各子表中的第一个记录和最后一个记录。
*/
void Distribute(SLCell *r, int i, ArrType f, ArrType e)
{
int j = ;
//各子表初始化为空
for(j=; j<RADIX; j++)
f[j] = e[j] = ; int p = ;
for(p=r[].next; p; p=r[p].next){
j = r[p].keys[i];
if(!f[j])
f[j] = p;
else
r[e[j]].next = p;
//将p所指的结点插入第j个字表中
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=; !f[j]; j++);
//r[0].next指向第一个非空子表的第一个结点
r[].next = f[j];
//t指向第一个非空子表的最后结点
t = e[j];
while(j<RADIX){
//找下一个非空子表
for(j+=; !f[j]; j++);
//链接两个非空子表
if(j<RADIX && f[j]){
r[t].next = f[j];
t = e[j];
}
}
//t指向最后一个非空子表中的最后一个结点
r[t].next = ;
} /*
* L是采用静态链表表示的顺序表。
* 对L作基数排序,使得L成为按关键字自小到大的有效静态链表,L->r[0]为头结点
*/
void RadixSort(SLList *L)
{
int i = ;
//将L改造成静态链表
for(i=; i<L->recnum; i++)
L->r[i].next = i+;
L->r[L->recnum].next = ;
#ifdef DEBUG
printf("将L改造成静态链表\n");
PrintSList(*L);
#endif ArrType f, e;
//按最低位优先依次对各关键字进行分配和收集
for(i=; i<L->keynum; i++){
//第i趟分配
Distribute(L->r, i, f, e);
#ifdef DEBUG
printf("第%d趟分配---------------------------------------\n");
PrintSList(*L);
printf("头指针队列:");
PrintArr(f, RADIX);
printf("尾指针队列:");
PrintArr(e, RADIX);
#endif
//第i躺收集
Collect(L->r, i, f, e);
#ifdef DEBUG
printf("第%d趟收集----\n");
PrintSList(*L);
printf("按next打印:");
int p = ;
for(p=L->r[].next; p; p=L->r[p].next){
printf("%d%d%d ", L->r[p].keys[], L->r[p].keys[], L->r[p].keys[]);
}
printf("\n");
#endif
}
} int getRedFromStr(char str[], int i, SLCell *result)
{
int key = atoi(str);
if(key< || key >){
printf("Error:too big!\n");
return -;
}
int units = , tens = , huns = ;
//百位
huns = key/;
//十位
tens = (key-*huns)/;
//个位
units = (key-*huns-*tens)/;
result->keys[] = units;
result->keys[] = tens;
result->keys[] = huns;
result->otheritems = 'a'+i-;
return ;
} int main(int argc, char *argv[])
{
SLList L;
int i = ;
for(i=; i<argc; i++){
if(i>MAX_SPACE)
break;
if(getRedFromStr(argv[i], i, &L.r[i]) < ){
printf("Error:only 0-999!\n");
return -;
}
}
L.keynum = ;
L.recnum = i-;
L.r[].next = ;
L.r[].otheritems = '';
RadixSort(&L);
return ;
}

链式基数排序

运行

内部排序->基数排序->链式基数排序的更多相关文章

  1. C语言链表全操作(增,删,改,查,逆序,递增排序,递减排序,链式队列,链式栈)

    一,数据结构——链表全操作: 链表形式: 其中,每个节点(Node)是一个结构体,这个结构体包含数据域,指针域,数据域用来存放数据,指针域则用来指向下一个节点: 特别说明:对于单链表,每个节点(Nod ...

  2. 内部排序->其它->地址排序(地址重排算法)

    文字描述 当每个记录所占空间较多,即每个记录存放的除关键字外的附加信息太大时,移动记录的时间耗费太大.此时,就可以像表插入排序.链式基数排序,以修改指针代替移动记录.但是有的排序方法,如快速排序和堆排 ...

  3. 程序员必知的8大排序(四)-------归并排序,基数排序(java实现)

    程序员必知的8大排序(一)-------直接插入排序,希尔排序(java实现) 程序员必知的8大排序(二)-------简单选择排序,堆排序(java实现) 程序员必知的8大排序(三)-------冒 ...

  4. 排序算法七:基数排序(Radix sort)

    上一篇提到了计数排序,它在输入序列元素的取值范围较小时,表现不俗.但是,现实生活中不总是满足这个条件,比如最大整形数据可以达到231-1,这样就存在2个问题: 1)因为m的值很大,不再满足m=O(n) ...

  5. HDU-2647 Reward(链式前向星+拓扑排序)

    Reward Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submis ...

  6. 数据结构与算法-排序(九)基数排序(Radix Sort)

    摘要 基数排序是进行整数序列的排序,它是将整数从个位开始,直到最大数的最后一位截止,每一个进位(比如个位.十位.百位)的数进行排序比较. 每个进位做的排序比较是用计数排序的方式处理,所以基数排序离不开 ...

  7. 七内部排序算法汇总(插入排序、Shell排序、冒泡排序、请选择类别、、高速分拣合并排序、堆排序)

    写在前面: 排序是计算机程序设计中的一种重要操作,它的功能是将一个数据元素的随意序列,又一次排列成一个按keyword有序的序列.因此排序掌握各种排序算法很重要. 对以下介绍的各个排序,我们假定全部排 ...

  8. ThinkPHP 数据库操作(三) : 查询方法、查询语法、链式操作

    查询方法 条件查询方法 where 方法 可以使用 where 方法进行 AND 条件查询: Db::table('think_user') ->where('name','like','%th ...

  9. Java实现各种内部排序算法

    数据结构中常见的内部排序算法: 插入排序:直接插入排序.折半插入排序.希尔排序 交换排序:冒泡排序.快速排序 选择排序:简单选择排序.堆排序 归并排序.基数排序.计数排序 直接插入排序: 思想:每次将 ...

随机推荐

  1. linux每日命令(28):chgrp命令

    在linux系统里,文件或目录的权限的掌控以拥有者及所属群组来管理.可以使用chgrp指令取变更文件与目录所属群组,这种方式采用群组名称或群组识别码都可以.Chgrp命令就是change group的 ...

  2. Asp.Net 合并图片(二维码和其他图片合并)

    需求:根据网址生成二维码,然后再把二维码生成到背景图片上,然后显示在网页中 pic.ashx <%@ WebHandler Language="C#" Class=" ...

  3. 设计模式---策略模式Strategy(对象行为型)

    1. 概述 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. 策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不 ...

  4. iOS应用开发,全局强制竖屏,部分页面允许旋转的处理

    目前大多数app都不支持横屏模式,用户不常用,开发起来也麻烦一些.但有些时候,又离不开横屏和竖屏的切换,比如查看视频.图片.文档等,这时又不得不对页面做横屏的处理.下面来教大家如何处理这种场景,方法可 ...

  5. Ramda函数式编程之PHP

    0x00 何为函数式编程 网上已经有好多详细的接受了,我认为比较重要的有: 函数是"第一等公民",即函数和其它数据类型一样处于平等地位 使用"表达式"(指一个单 ...

  6. Java知多少(71)文件与目录管理

    目录是管理文件的特殊机制,同类文件保存在同一个目录下不仅可以简化文件管理,而且还可以提高工作效率.Java 语言在 java.io 包中定义了一个 File 类专门用来管理磁盘文件和目录. 每个 Fi ...

  7. Java知多少(72)文件的随机读写

    Java.io 包提供了 RandomAccessFile 类用于随机文件的创建和访问.使用这个类,可以跳转到文件的任意位置读写数据.程序可以在随机文件中插入数据,而不会破坏该文件的其他数据.此外,程 ...

  8. azkaban在centos下的部署安装

    azkaban 是一个用Java开发的开源调度工具workflow. 下面介绍具体安装过程,我这里使用的版本是3.43.0,使用的是solo运行模式. 编译 git clone https://git ...

  9. Python 的 Magic Methods 指南(转)

    介绍 本指南是数月博客的总结.主题是魔术方法. 什么是魔术方法呢?它们是面向对象Python语言中的一切.它们是你可以自定义并添加“魔法”到类中的特殊方法.它们被双下划线环绕(比如__init__或_ ...

  10. tensorflow模型量化

    tensorflow模型量化/DATA/share/DeepLearning/code/tensorflow/bazel-bin/tensorflow/tools/graph_transforms/t ...