本系列文章为浙江大学陈越、何钦铭数据结构学习笔记,前面的系列文章链接如下
数据结构基础:P1-基本概念
数据结构基础:P2.1-线性结构—>线性表
数据结构基础:P2.2-线性结构—>堆栈
数据结构基础:P2.3-线性结构—>队列
数据结构基础:P2.4-线性结构—>应用实例:多项式加法运算
数据结构基础:P2.5-线性结构—>应用实例:多项式乘法与加法运算-C实现
数据结构基础:P3.1-树(一)—>树与树的表示
数据结构基础:P3.2-树(一)—>二叉树及存储结构
数据结构基础:P3.3-树(一)—>二叉树的遍历
数据结构基础:P3.4-树(一)—>小白专场:树的同构-C语言实现
数据结构基础:P4.1-树(二)—>二叉搜索树
数据结构基础:P4.2-树(二)—>二叉平衡树
数据结构基础:P4.3-树(二)—>小白专场:是否同一棵二叉搜索树-C实现
数据结构基础:P4.4-树(二)—>线性结构之习题选讲:逆转链表
数据结构基础:P5.1-树(三)—>堆
数据结构基础:P5.2-树(三)—>哈夫曼树与哈夫曼编码
数据结构基础:P5.3-树(三)—>集合及运算
数据结构基础:P5.4-树(三)—>入门专场:堆中的路径
数据结构基础:P5.5-树(三)—>入门专场:File Transfer
数据结构基础:P6.1-图(一)—>什么是图
数据结构基础:P6.2-图(一)—>图的遍历
数据结构基础:P6.3-图(一)—>应用实例:拯救007
数据结构基础:P6.4-图(一)—>应用实例:六度空间
数据结构基础:P6.5-图(一)—>小白专场:如何建立图-C语言实现
数据结构基础:P7.1-图(二)—>树之习题选讲:Tree Traversals Again
数据结构基础:P7.2-图(二)—>树之习题选讲:Complete Binary Search Tree
数据结构基础:P7.3-图(二)—>树之习题选讲:Huffman Codes
数据结构基础:P7.4-图(二)—>最短路径问题
数据结构基础:P7.5-图(二)—>哈利·波特的考试
数据结构基础:P8.1-图(三)—>最小生成树问题
数据结构基础:P8.2-图(三)—>拓扑排序
数据结构基础:P8.3-图(三)—>图之习题选讲-旅游规划
数据结构基础:P9.1-排序(一)—>简单排序(冒泡、插入)
数据结构基础:P9.2-排序(一)—>希尔排序
数据结构基础:P9.3-排序(一)—>堆排序
数据结构基础:P9.4-排序(一)—>归并排序
数据结构基础:P10.1-排序(二)—>快速排序
数据结构基础:P10.2-排序(二)—>表排序

文章目录

  • 前言
  • 一、桶排序
  • 二、基数排序
  • 三、多关键字的排序
  • C语言代码:基数排序-次位优先
  • C语言代码:基数排序-主位优先
  • 小测验

前言

前面我们讲了那么多排序算法,它们有一个共同的特点:他们仅仅是基于比较大小来决定元素位置的。那么有定理结论告诉我们,仅仅基于比较进行的排序算法它们的最坏时间复杂度下界是 O ( N l o g N ) \rm{O(NlogN)} O(NlogN)。也就是说,我们总能制造出一个最坏情况让它用最快的算法跑,它也只能跑到NlogN。那么还有没有可能更快呢?当然有可能!就是我们除了比较之外还干点什么别的事,这就是基数排序

一、桶排序

在介绍基数排序之前,我们先来看一下桶排序,事实上基数排序是桶排序的升级版。

例子

题目:假设我们有N个学生,他们的成绩是0到100之间的整数(于是有M=101个不同的成绩值) 。如何在线性时间内将学生按成绩排序?
解决思路
①我们为每一个成绩值构造一个桶,于是我就建了101个桶。换言之我就是在程序里面设了一个数组count,这个数组的每一个元素是一个指针,它一开始被初始化为一个空链表的头指针,所以我们一开始有了101个空链表,也就对应了101个空的桶。

②当第i个学生进来的时候,比如说有一个学生考了88分,我们就先找到88这个桶,然后把这个学生的信息插到这个链表的表头里。

算法对应伪码如下:

void Bucket_Sort(ElementType A[], int N)
{ count[]初始化;while (读入1个学生成绩grade)将该生插入count[grade]链表;for ( i=0; i<M; i++ ) {if ( count[ ( count[i] )输出整个count[i]链表;}
}

时间复杂度

T ( N , M ) = O ( M + N ) \rm{T(N, M) = O( M+N )} T(N,M)=O(M+N)
①在插入的时候,因为每一次我都插到表头,所以是一个常数倍的时间。我把N个学生的信息逐个插到相应的表里去,那就是一个 O ( N ) \rm{O(N)} O(N)的时间。我输出的时候,我还需要扫描每一个桶,这一共有M个桶,所以还要加上M的时间。所以是 O ( M + N ) \rm{O(M+N)} O(M+N)
②当M非常小的时候,比如说有4万个学生,只有101个不同成绩值的时候,那这个其实就是一种线性的算法了。

二、基数排序

对于上面那个例子,我们有一个问题:如果M要比N要大很多,该怎么办呢?

例子

题目:假设我们有N=10个整数,每个整数的值在0到999之间(于是有M=1000个不同的值)。还有可能在线性时间内排序吗?
解决思路
N个整数最大就是三位数,并且它们是十进制,基数就是10,利用这些特征我们来建桶。
假设现在我们输入的10个数是:64, 8, 216, 512, 27, 729, 0, 1, 343, 125
我们用次位优先(Least Significant Digit)的思想来排序,即根据最低位的大小来排序,比如216和27,我们认为27的次位大于216的次位。
①首先,基数为10,建立10个桶

②在第一趟排序的时候,我们按照它们的个位数把它们放到相应的桶里去

③在第二趟排序的时候,我们按照它们的十位数把它们放到相应的桶里去

④在第三趟排序的时候,我们按照它们的百位数把它们放到相应的桶里去

⑤我们将所有的桶从前到后串在一起,就得到:0,1,8,27,64,125,216,343,512,729。整个数列就是有序的了。

时间复杂度

T = O ( P ( N + B ) ) \rm{T=O(P(N+B))} T=O(P(N+B))
我们一共经历了P趟排序,在每一趟排序里面我首先要把N个数全部都收集起来并分配一遍。收集分配的过程跟桶的个数是有关系的,至少要访问建立的B个桶。所以整体复杂度是:P(N+B)。当桶的个数非常小的话,基本是线性复杂度,比正常的排序效果好。

三、多关键字的排序

基数排序不仅仅是用于处理整数的基数排序,它还可以被用于处理有多关键字的排序。

例子

一副扑克牌就是按两种关键字来排序的,主关键字叫做花色,次关键字是面值。新牌拿到手的时候,就是按照下面的顺序排列的。基数排序在处理多关键字排序的时候,实际上可以每一种关键字理解成为那个整数里面的某一位。比如说次关键字就对应的是它的个位,主关键字就对应的是它的高位。那我有两种排序方法:主位优先和次位优先。

①我们根据主位优先(Most Significant Digit First)来建立桶,于是就把50多张牌往对应花色桶里面放。

②然后我们在每一个桶里进行排序,将数值顺序调整正确,最后来合并结果。
问题:我们还是需要在每个桶里面调用相应的排序算法来解决面值的排序,但是总体的时间复杂度一般来说会比你把它完整的看成一个待排数组来排要稍微好一点。但是,显然这个主位优先在这里面这么排不是很聪明。

使用次位优先

①根据面值建立13个桶
②然后把这50多张牌分别往这个13个桶里面丢,每个桶对应的是一种面值

③我下一步只要把结果合并起来,然后再为花色建四个桶。接着,我根本不需要排序,只需要从上到下把这些牌按照它的花色
放到相应的花色的桶里面去,最后再收集一下,把结果一合并就得到了我们最后有序的序列。

C语言代码:基数排序-次位优先

/* 基数排序 - 次位优先 *//* 假设元素最多有MaxDigit个关键字,基数全是同样的Radix */
#define MaxDigit 4
#define Radix 10/* 桶元素结点 */
typedef struct Node *PtrToNode;
struct Node {int key;PtrToNode next;
};/* 桶头结点 */
struct HeadNode {PtrToNode head, tail;
};
typedef struct HeadNode Bucket[Radix];int GetDigit ( int X, int D )
{ /* 默认次位D=1, 主位D<=MaxDigit */int d, i;for (i=1; i<=D; i++) {d = X % Radix;X /= Radix;}return d;
}void LSDRadixSort( ElementType A[], int N )
{ /* 基数排序 - 次位优先 */int D, Di, i;Bucket B;PtrToNode tmp, p, List = NULL; for (i=0; i<Radix; i++) /* 初始化每个桶为空链表 */B[i].head = B[i].tail = NULL;for (i=0; i<N; i++) { /* 将原始序列逆序存入初始链表List */tmp = (PtrToNode)malloc(sizeof(struct Node));tmp->key = A[i];tmp->next = List;List = tmp;}/* 下面开始排序 */ for (D=1; D<=MaxDigit; D++) { /* 对数据的每一位循环处理 *//* 下面是分配的过程 */p = List;while (p) {Di = GetDigit(p->key, D); /* 获得当前元素的当前位数字 *//* 从List中摘除 */tmp = p; p = p->next;/* 插入B[Di]号桶尾 */tmp->next = NULL;if (B[Di].head == NULL)B[Di].head = B[Di].tail = tmp;else {B[Di].tail->next = tmp;B[Di].tail = tmp;}}/* 下面是收集的过程 */List = NULL; for (Di=Radix-1; Di>=0; Di--) { /* 将每个桶的元素顺序收集入List */if (B[Di].head) { /* 如果桶不为空 *//* 整桶插入List表头 */B[Di].tail->next = List;List = B[Di].head;B[Di].head = B[Di].tail = NULL; /* 清空桶 */}}}/* 将List倒入A[]并释放空间 */for (i=0; i<N; i++) {tmp = List;List = List->next;A[i] = tmp->key;free(tmp);}
}

C语言代码:基数排序-主位优先

* 基数排序 - 主位优先 *//* 假设元素最多有MaxDigit个关键字,基数全是同样的Radix */#define MaxDigit 4
#define Radix 10/* 桶元素结点 */
typedef struct Node *PtrToNode;
struct Node{int key;PtrToNode next;
};/* 桶头结点 */
struct HeadNode {PtrToNode head, tail;
};
typedef struct HeadNode Bucket[Radix];int GetDigit ( int X, int D )
{ /* 默认次位D=1, 主位D<=MaxDigit */int d, i;for (i=1; i<=D; i++) {d = X%Radix;X /= Radix;}return d;
}void MSD( ElementType A[], int L, int R, int D )
{ /* 核心递归函数: 对A[L]...A[R]的第D位数进行排序 */int Di, i, j;Bucket B;PtrToNode tmp, p, List = NULL; if (D==0) return; /* 递归终止条件 */for (i=0; i<Radix; i++) /* 初始化每个桶为空链表 */B[i].head = B[i].tail = NULL;for (i=L; i<=R; i++) { /* 将原始序列逆序存入初始链表List */tmp = (PtrToNode)malloc(sizeof(struct Node));tmp->key = A[i];tmp->next = List;List = tmp;}/* 下面是分配的过程 */p = List;while (p) {Di = GetDigit(p->key, D); /* 获得当前元素的当前位数字 *//* 从List中摘除 */tmp = p; p = p->next;/* 插入B[Di]号桶 */if (B[Di].head == NULL) B[Di].tail = tmp;tmp->next = B[Di].head;B[Di].head = tmp;}/* 下面是收集的过程 */i = j = L; /* i, j记录当前要处理的A[]的左右端下标 */for (Di=0; Di<Radix; Di++) { /* 对于每个桶 */if (B[Di].head) { /* 将非空的桶整桶倒入A[], 递归排序 */p = B[Di].head;while (p) {tmp = p;p = p->next;A[j++] = tmp->key;free(tmp);}/* 递归对该桶数据排序, 位数减1 */MSD(A, i, j-1, D-1);i = j; /* 为下一个桶对应的A[]左端 */} }
}void MSDRadixSort( ElementType A[], int N )
{ /* 统一接口 */MSD(A, 0, N-1, MaxDigit);
}

小测验

1、基数排序是稳定的算法 (正确)

数据结构基础:P10.3-排序(二)--->基数排序相关推荐

  1. 数据结构基础:P4.2-树(二)--->二叉平衡树

    本系列文章为浙江大学陈越.何钦铭数据结构学习笔记,前面的系列文章链接如下: 数据结构基础:P1-基本概念 数据结构基础:P2.1-线性结构->线性表 数据结构基础:P2.2-线性结构->堆 ...

  2. 数据结构基础知识核心归纳(一)

    数据结构基础知识核心归纳(一) 转载请声明出处:http://blog.csdn.net/andrexpert/article/details/77900395 Android             ...

  3. 数据结构源码笔记(C语言):英文单词按字典序排序的基数排序

    //实现英文单词按字典序排序的基数排序算法#include<stdio.h> #include<malloc.h> #include<string.h>#defin ...

  4. SDUT 3399 数据结构实验之排序二:交换排序

    数据结构实验之排序二:交换排序 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 冒泡排序和快 ...

  5. java 线性的排序算法_数据结构之排序算法Java实现(9)—— 线性排序之 基数排序算法...

    基数排序算法是计数排序的延伸,计数排序会造成很大的空间浪费,但基数排序法是对位数进行排序,适合于位数之间相差较大的情况,废话不多说,直接上代码: 升序排序法: /** * 基数排序法 * 升序排列 * ...

  6. Kiner算法刷题记(二十一):字典树与双数组字典树(数据结构基础篇)

    字典树与双数组字典树(数据结构基础篇) 系列文章导引 系列文章导引 开源项目 本系列所有文章都将会收录到GitHub中统一收藏与管理,欢迎ISSUE和Star. GitHub传送门:Kiner算法算题 ...

  7. 数据结构排序算法实验报告_[数据结构与算法系列]排序算法(二)

    我的上一篇文章向大家介绍了排序算法中的冒泡排序.插入排序和选择排序.它们都是平均时间复杂度为 O(n^2) 的排序算法,同时还为大家讲解了什么是原地排序和什么是排序的稳定性.下图是这三种算法的比较,不 ...

  8. 【数据结构笔记38】桶排序、基数排序、多关键字排序、排序算法汇总比较

    本次笔记内容: 10.3.1 桶排序 10.3.2 基数排序 10.3.3 多关键字排序 10.4 排序算法比较 文章目录 排序算法背景 桶排序 基数排序 多关键字排序(基数排序) 排序算法的比较 排 ...

  9. 一周刷爆LeetCode,算法da神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解 笔记

    一周刷爆LeetCode,算法大神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解 笔记 教程与代码地址 P1 出圈了!讲课之外我们来聊聊 ...

  10. 02_Python算法+数据结构笔记-冒泡排序-选择排序-插入排序-快排-二叉树

    b站视频:路飞IT学城 清华计算机博士带你学习Python算法+数据结构_哔哩哔哩_bilibili 文章目录 #11 排序介绍 #12 冒泡排序介绍 #13 冒泡排序 #14 选择排序 #15 插入 ...

最新文章

  1. Dynamics CRM2013 自定义开发工具 Dynamics XRM Tools 介绍
  2. 主流mes厂商_MES市场的前景
  3. 阻塞队列的使用案例-注册成功后增加积分
  4. 美丽新世界:当代日本视觉文化展
  5. STM32之GPIO浮空输入例程
  6. c语言能让制表符空9个字符码,C语言入门9-1-分类函数
  7. S5PV210体系结构与接口11:NandFlash SD卡编程
  8. ylbtech-dbs:ylbtech-7,welfareSystem(福利发放系统)
  9. xp系统启动sql服务器,XP系统,sqlserver服务启动了又自己停止,服务管理器点了启动随后马上就已停止!...
  10. 计算机连接拒绝访问,打印机拒绝访问,教您打印机拒绝访问怎么解决
  11. php怎么分栏,word文档怎么设置分栏格式
  12. JS实现保存当前页面
  13. Delphi的编程语言Object Pascal(3)
  14. 多光谱行人检测(一)Multispectral Pedestrian Detection:Benchmark Dataset and Baseline
  15. windows10桌面计算机图标删除吗,win10电脑桌面图标删除不了怎么办
  16. 倾角传感器组成和应用
  17. 统计学习方法-李航-第一章:统计学习方法概论-笔记1
  18. 【计算机组成原理】电路基本原理、加法器的设计
  19. 描述性统计分析都用到哪些可视化图表?
  20. 2019计算机考研攻略,2019年重大计算机学硕考研经验谈

热门文章

  1. 动手实践bert+BiLstm+crf
  2. 新基建助力智能化道路交通领域的转型发展
  3. 视频压缩:谷歌基于GAN实现
  4. 王利芬对话IT大佬,
  5. USART学习(一)
  6. 西门子mr图像后处理手册_西门子MRI图像文本信息
  7. 在应用处理器上开发实时任务系统
  8. IDEA离线安装JRebel插件并激活
  9. CSS学习--浏览器应用
  10. Java获取时间范围: 当前季度,上个季度,昨天,当前月,上个月