【数据结构】 归并排序、 基数排序

news/2024/4/19 11:40:19/文章来源:https://blog.csdn.net/zsy3757486/article/details/127239684

目录

一、什么是归并排序?

二、归并排序

三、什么是基数排序?

四、基数排序

五、各种排序的比较


一、什么是归并排序?

归并排序是建立在归并操作上的一种有效,稳定的排序算法。是将已有序的子序列合并得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

二、归并排序

1 . 概述

  • 归并排序,Merging Sort

  • 归并排序:两个或两个以上有序表合并成一个新的有序表

  • 归并排序分类:

    1. 二路归并排序

    2. 多路归并排序

  • 二路归并排序:两个有序表合并成一个有序表的归并排序,称为二路归并排序。也就是将两个相邻的记录有序子序列归并为一个记录的有序序列。

  • 二路归并排序基本思想:

    1. 将待排序记录r[0]到r[n-1]看成是n个长度为1的有序子表

    2. 把这些子表依次两两归并,便得到[n/2]个有序子表

    3. 然后,再把这[n/2]个有序的子表进行两两归并

    4. 如此重复,直到最后得到一个长度为n的有序表为止

2 .两个相邻有序序列归并:分析

  • 假设前后两个有序序列分别存放在一维数组人的r[h..m] 和 r[m+1..t]中,同时,提供另一个有序数组order,用于存放归并后的数据

  • 首先在两个有序序列中,分别从第1个记录开始进行对应关键字的比较,将关键字值较小的记录放入有序数据order中

  • 然后,依次对两个有序序列中剩余记录进行相同操作,直到两个有序序列中的所有记录都加入到有序数组order中为止

  • 最后,这个有序数组order中存放的记录序列就是归并排序后的结果。

3. 两个相邻有序序列归并:算法

  • 代码

    /**** @param r 待排序的数组(多个有序子序列)* @param order 已经排序号的数组* @param h 第一个子序列开始的位置* @param m 第一个子序列结束的位置,第二个子序列开始的位置为m+1* @param t 第二个子序列结束的位置*/
    //【算法】两个有序序列的归并算法
    //把r数组中两个相邻的有序表r[h]~r[m]和r[m+1]~r[t]归并为一个有序表order[h]~order[t]
    public void merge(RecordNode[] r, RecordNode[] order, int h, int m, int t) {int i = h, j = m + 1, k = h;while (i <= m && j <= t) {                  // 将r中两个相邻子序列归并到order中if (r[i].key.compareTo(r[j].key) <= 0) {// 较小值复制到order中order[k++] = r[i++];} else {order[k++] = r[j++];}}while (i <= m) {                // 将前一个子序列剩余元素复制到order中order[k++] = r[i++];}while (j <= t) {                // 将后一个子序列剩余元素复制到order中order[k++] = r[j++];}
    }
  • 测试
​
public class TestSeqList13_merge {public static void main(String[] args) throws Exception {int[] arr = {39,52,67,95,8,25,56,70};SeqList seqList = new SeqList(arr.length);System.out.print("序列号:");for (int i = 0; i < arr.length; i++) {System.out.print("  " + i);seqList.insert(i, new RecordNode(arr[i]));}System.out.println();System.out.print("归并前:");seqList.display();// 归并操作System.out.print("归并后:");RecordNode[] temp = new RecordNode[arr.length];// 待归并的数据 {39,52,67,95}和 {8,25,56,70}seqList.merge(seqList.r,temp,0,3, 7);// 输出归并后的数据for (int i = 0; i < temp.length; i++) {String str = temp[i].key.toString().length() == 1 ? "  " : " ";System.out.print(str + temp[i].key.toString());}System.out.println();}
}
//归并操作
//序列号:  0  1  2  3  4  5  6  7
//归并前: 39 52 67 95  8 25 56 70
//归并后:  8 25 39 52 56 67 70 95

4. 一趟归并:分析

  • 假设r为待排序的数组,n为待排序列的长度,s为待归并的有序子序列的长度,一趟归并排序的结果存放在数组order中。

  • 两个相邻的有序序列,第一趟处理的长度为1,第二趟为2,第三趟为4 ... ,也就是说s的取值1/2/4/8...

  • 步骤:

    1. 根据长度s,进行两两归并

    2. 归并操作时,如果最后两个序列长度不等,将剩余内容归并到有序表中

    3. 归并操作时,如果有未参加归并操作的内容,直接复制到order中

5. 一趟归并:算法

  • 代码

    //【算法】一趟归并算法
    //把数组r[n]中每个长度为s的有序表两两归并到数组order[n]中
    //s 为子序列的长度,n为排序序列的长度
    public void mergepass(RecordNode[] r, RecordNode[] order, int s, int n) {System.out.print("子序列长度s=" + s + " ");int p = 0;  //p为每一对待合并表的第一个元素的下标,初值为0while (p + 2 * s - 1 <= n - 1) {  //两两归并长度均为s的有序表merge(r, order, p, p + s - 1, p + 2 * s - 1);p += 2 * s;}if (p + s - 1 < n - 1) {  //归并最后两个长度不等的有序表merge(r, order, p, p + s - 1, n - 1);} else {for (int i = p; i <= n - 1; i++) //将剩余的有序表复制到order中{order[i] = r[i];}}
    }

测试:s=1

public class TestSeqList14_mergepass {public static void main(String[] args) throws Exception {int[] arr = {52,39,67,95,70,8,25,52,56};SeqList seqList = new SeqList(arr.length);System.out.print("序列号:\t\t ");for (int i = 0; i < arr.length; i++) {System.out.print("  " + i);seqList.insert(i, new RecordNode(arr[i]));}System.out.println();System.out.print("初始值:\t\t ");seqList.display();// 一趟归并算法RecordNode[] temp = new RecordNode[arr.length];int s = 1;seqList.mergepass(seqList.r, temp, s, arr.length);for (int i = 0; i < temp.length; i++) {String str = temp[i].key.toString().length() == 1 ? "  " : " ";System.out.print(str + temp[i].key.toString());}System.out.println();
​}
}
//一趟归并算法
//序列号:       0  1  2  3  4  5  6  7  8
//初始值:      52 39 67 95 70  8 25 52 56
//子序列长度s=1  39 52 67 95  8 70 25 52 56

  • 测试:s=8
public class TestSeqList14_mergepass2 {public static void main(String[] args) throws Exception {int[] arr = {8,25,39,52,52,67,70,95,56};SeqList seqList = new SeqList(arr.length);System.out.print("序列号:\t\t ");for (int i = 0; i < arr.length; i++) {System.out.print("  " + i);seqList.insert(i, new RecordNode(arr[i]));}System.out.println();System.out.print("初始值:\t\t ");seqList.display();// 一趟归并算法RecordNode[] temp = new RecordNode[arr.length];int s = 8;seqList.mergepass(seqList.r, temp, s, arr.length);for (int i = 0; i < temp.length; i++) {String str = temp[i].key.toString().length() == 1 ? "  " : " ";System.out.print(str + temp[i].key.toString());}System.out.println();
​}
}
//一趟归并算法
//序列号:       0  1  2  3  4  5  6  7  8
//初始值:       8 25 39 52 52 67 70 95 56
//子序列长度s=8   8 25 39 52 52 56 67 70 95

6 二路归并:分析

  • 设置待排序的n个记录保存在数组r[n]中,归并过程中需要引入辅助数组temp[n],

  • 第1趟由r归并到temp,第2趟由temp归并到r

  • 如此反复,直到n个记录成为一个有序表为止。

  • 在归并过程中,为了将最后的排序结果仍置于数组中,需要进行的归并趟数为偶数,

  • 如果实际上只需奇数趟即可生成,那么最后还要进行一趟,正好此时temp中的n个有序记录,为一个长度不大于s的表,将会背直接复制r。

7. 二路归并: 算法

  • 添加打印方法

    public void display(RecordNode[] arr) {    //输出数组元素for (int i = 0; i < arr.length; i++) {String str = arr[i].key.toString().length() == 1 ? "  " : " ";System.out.print(str + arr[i].key.toString());}System.out.println();
    }
  • 算法代码

    //【算法】2-路归并排序算法
    public void mergeSort() {System.out.println("归并排序");int s = 1;                              // s为已排序的子序列长度,初值为1int n = this.curlen;RecordNode[] temp = new RecordNode[n];  // 定义长度为n的辅助数组tempwhile (s < n) {mergepass(r, temp, s, n);           // 一趟归并,将r数组中各子序列归并到temp中display(temp);                      // 打印temp临时数组s *= 2;                             // 子序列长度加倍,下一趟归并mergepass(temp, r, s, n);           // 将temp数组中各子序列再归并到r中display();                          // 打印r数组s *= 2;}
    }
  • 测试
public class TestSeqList15_mergeSort {public static void main(String[] args) throws Exception {int[] arr = {52,39,67,95,70,8,25,52,56};SeqList seqList = new SeqList(arr.length);System.out.print("序列号:\t\t ");for (int i = 0; i < arr.length; i++) {System.out.print("  " + i);seqList.insert(i, new RecordNode(arr[i]));}System.out.println();System.out.print("初始值:\t\t ");seqList.display();// 二路归并算法seqList.mergeSort();
​}
}
//二路归并算法
//序列号:      0  1  2  3  4  5  6  7  8
//初始值:      52 39 67 95 70  8 25 52 56
//归并排序
//子序列长度s=1  39 52 67 95  8 70 25 52 56
//子序列长度s=2  39 52 67 95  8 25 52 70 56
//子序列长度s=4   8 25 39 52 52 67 70 95 56
//子序列长度s=8   8 25 39 52 52 56 67 70 95

8. 性能分析

  • 时间复杂度:

    1. 归并趟数:log2n

    2. 每一趟时间复杂度:O(n)

    3. 二路归并排序:O(nlog2n)

  • 空间复杂度:O(n)

  • 二路归并是一个==稳定==的排序算法


三、什么是基数排序?

基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort。它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。


四、基数排序

1 概述

  • 基数排序(Radix Sort):是一种借助于多关键字进行排序,也就是一种将单关键字按基数分成“多关键字”进行排序的方法。

  • 基数排序分类:

    • 多关键字排序

    • 链式基数排序


2 多关键字排序

  • n个记录的序列 {R1, R2, ... , Rn}对关键字(k1, k2, ... , kd)有序。

  • 就是说,对于序列中任意两个记录Ri和Rj (1≤i<j≤n) 都满足下列有序关系:

    (ki1, ki2, ... , kid) < (kj1, kj2, ... , kjd)

    1. K1称为最主位关键字

    2. Kd称为最主位关键字

  • 多关键字排序按照关键字的顺序分为两种方式:

    1. 最主位优先MSD法:先对K1进行分组,同一组中记录,若关键字K1相等,再对各组按K2排序分成子组,... , 至最后对最次位关键字排序完成为止。

    2. 最次为优先LSD法:先对Kd进行排序,然后对Kd-1进行排序,依次类推,直至对主要位关键字K1排序完成为止。(排序过程中不需要根据“前一个”关键字的排序结果,将记录序列分隔成若干个子序列)

  • 例如:学生记录含三个关键字,系别班号班内的序列号其中以系别为最主位关键字。


3. 链式基数排序

  • 假如多关键字的记录序列中,每个关键字的取值范围相同,则按LSD法进行排序时,可以采用“分配-收集”的方法,其好处是不需要进行关键字间的比较。

  • 对于数字型或字符型的单关键字,可以看成是由多个数位或多个字符构成的多关键字,此时可以采用这种“分配-收集”的办法进行排序,称为基数排序法。

  • 例如:关键字如下{209, 386, 768, 185, 247, 606, 230, 834, 539}

    1. 首先按其个位数取值分别为0,1,...9分配成10组,之后按从0至9的顺序将他们收集在一起

    2. 然后按其十位数取值分别为0,1,...9分配成10组,之后再按从0至9的顺序将他们收集在一起

    3. 最后按其百位数重复一遍上述操作

  • 在计算机上实现基数排序时,为减少所需辅助存储空间,应采用链表作存储结构,即链式基数排序,具体操作:

    1. 待排序记录以指针相链,构成一个链表

    2. 分配时,按当前关键字位所取值,将记录分配到不同的链队列中,每个队列中记录的关键字位相同

    3. 收集时,按当前关键字位取值从小到大将各队列首尾相链成一个链表

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

例如:

注意:

  1. 分配和收集的实际操作仅为修改链表中的指针和设置队列的头、尾指针

  2. 为查找使用,该链表尚需将它调整为有序表。


五、各种排序的比较


写到最后

四季轮换,已经数不清凋零了多少, 愿我们往后能向心而行,一路招摇胜!

🐋 你的支持认可是我创作的动力

💟 创作不易,不妨点赞💚评论❤️收藏💙一下

😘 感谢大佬们的支持,欢迎各位前来不吝赐教

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_398838.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

09-Pawn类 UE4 C++

1.首先创建一个C的Pawn类 右键点击Public&#xff0c;选择新建C类 选择Pawn&#xff0c;然后点击下一步 命名后&#xff0c;点击创建 创建完毕&#xff0c;双击打开MyPawn 2.在MyPawn.h中添加如下代码&#xff1a; UPROPERTY(EditAnywhere) class UStaticMeshComponent* Mes…

SpringCloud-31-Spring Cloud Config微服务与配置文件解耦

11.8 微服务与配置文件解耦 我们可以将之前的子模块中的配置提取出来&#xff0c;托管到gitee上统一管理&#xff0c;这样运维人员维护配置文件就不变动子模块了&#xff0c;实现了模块与配置的解耦。 下面用例子来解释下这种做法的好处 在基础工程spring-cloud-microservice…

热血江湖服务端架设开服搭建教程

热血江湖服务端架设开服搭建教程 玩网游比较多的小伙伴&#xff0c;相信对热血江湖这款游戏也不陌生&#xff0c;摆脱了传统武侠游戏阴暗血腥的游戏风格&#xff0c;提倡一种“明朗而愉快的武侠”精神。画面上即不会太随意又不会过于沉重&#xff0c;画面干净清新。活泼可爱的…

ORACLE新增数据库(用户),使用navicate

oracle新增数据库并不像mysql直接指令就行&#xff0c;百度一圈都是用Oracle Database Configuration Assistant的&#xff0c;其实navicate就直接可以新建&#xff0c;以下是新建方法&#xff1a; 1.连接数据库 2.新建表空间 点击navicate上方工具栏中"其它"&…

何为功能平价?特斯拉「抛弃」多传感融合,背后有哪些门道

技术与成本&#xff0c;永远是博弈的两方。 当大部分车企都在寻求通过增加更多、更高性能的传感器&#xff08;也就是通常所说的多传感融合技术&#xff09;来强化智能驾驶功能可靠性和拓展性的大背景下&#xff0c;特斯拉依然我行我素&#xff0c;继续沿着纯视觉感知的路线前…

盘点一个Python列表(元素多样)处理的实战题目(使用正则表达式也可以实现)

大家好,我是Python进阶者。 一、前言 前几天在Python白银交流群【凡人不烦人】问了一个Python列表处理的问题,提问截图如下:下面是他的部分数据: lst = [(问答题)(2) 假设镀锌钢管, http://admintk.sc.zzstep.com/UpLoadImage/2019-10-10/a84f340e-6c67-42b1-8eae-3dc14281…

队列的操作实验(数据结构)

队列的操作实验&#xff08;数据结构&#xff09; 一、实验目的 1&#xff0e;掌握队列存储结构的表示和实现方法。 2&#xff0e;掌握队列的入队和出队等基本操作的算法实现。 3&#xff0e;了解队列在解决实际问题中的简单应用。 二、实验内容 1&#xff0e;建立顺序循环队列…

【LeetCode】【二叉搜索树迭代器】

173. 二叉搜索树迭代器 实现一个二叉搜索树迭代器类BSTIterator &#xff0c;表示一个按中序遍历二叉搜索树&#xff08;BST&#xff09;的迭代器&#xff1a; BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指…

【优化充电】基于matlab粒子群算法电动汽车充电动态优化策略【含Matlab源码 2163期】

一、粒子群算法电动汽车充电优化 1 电动汽车充电负荷估算 电动汽车的充电负荷主要与电动汽车起始充电时刻和充电时长相关,而起始充电时刻是由电动汽车用户的到家时间决定的,充电时长主要与电动汽车的行驶里程和充电倍率相关。 目前电动汽车还没有大规模运营, 只能通过统计燃油…

ASP.NET Core微服务(六)——【.Net Core操作redis】StackExchange.Redis

ASP.NET Core微服务(六)——【.Net Core操作redis】StackExchange.Redis 目录 ASP.NET Core微服务(六)——【.Net Core操作redis】StackExchange.Redis 项目创建 StackExchange.Redis操作示例 引包【using StackExchange.Redis;】 ConnectionMultiplexer RedisDBHelper …

Git学习总结

目录&#xff1a; &#xff08;1&#xff09;版本控制 &#xff08;2&#xff09;Git和SVN的区别 &#xff08;3&#xff09;Git历史 &#xff08;4&#xff09;安装Git及环境配置 &#xff08;5&#xff09;常用的Linux命令 &#xff08;6&#xff09;Git的必要配置 &a…

PMO和PM如何实现从战略解码到项目执行的端到端闭环?

一、PMO的使命与职责 PMO的使命是提升端到端组织效能&#xff0c;赋能于精细化管理&#xff0c;成为企业的加速器&#xff0c;保障战略项目的交付。 那么PMO要保障战略的交付&#xff0c;核心职责有哪些呢&#xff1f; 二、组织为什么需要端到端项目管理&#xff1f; 核心价…

【ZooKeeper】ZooKeeper 应用场景

ZooKeeper 应用场景发布订阅命名服务集群管理分布式锁分布式队列管理负载均衡配置管理ZooKeeper&#xff1a;分布式协调服务&#xff0c;仲裁机构。基于ZNode数据模型和Watcher监听机制可以解决很多问题&#xff0c;比如分布式锁问题。 应用场景如下&#xff1a; 1、发布/订阅 …

servlet基础知识

早期的Web应用主要用于浏览新闻等静态页面&#xff0c;HTTP服务器&#xff08;比如 Apache、Nginx&#xff09;向浏览器返回静态 HTML&#xff0c;浏览器负责解析HTML&#xff0c;将结果呈现给用户。随着互联网的发展&#xff0c;还希望进行一些交互操作来获取动态结果&#xf…

Python Turtle绘图基础(一)——Turtle简介、绘图窗体与绘图区域

今天继续给大家介绍渗透测试相关知识&#xff0c;本文主要内容是Python Turtle绘图基础&#xff0c;包括Turtle简介、绘图窗体与绘图区域。 一、Turtle库简单介绍 Turtle库时Python语言的标准库&#xff08;所谓标准库&#xff0c;就是在安装Python时自带的库&#xff0c;与之…

【经典面试题-LeetCode69/剑指 Offer II 072:x 的平方根 (Python3实现)】

x 的平方根一、题目描述1.题目内容2.样例二、解决方案1.基本代码&#xff08;成功提交&#xff09;2.略微拓展一、题目描述 这是一道经典的面试题&#xff0c;需要我们在不使用任何内置函数的前提下&#xff0c;手动实现求指定整数的算术平方根。 1.题目内容 给你一个非负整数…

Android开发——底部导航栏设计

底部导航栏设计1.依赖配置2.tabbar的UI实现3.tabbar的逻辑绑定4.tabbar的滑动与点击联动其实,常见的Android和微信小程序一样&#xff0c;通常最下面一排需要有一排导航栏&#xff0c;可以通过点击导航栏图标和滑动实现页面跳转&#xff0c;具体实现使用的是Android的 ViewPage…

在MUI框架中对于事件绑定与取消和监听的触发自定义的深入运用与实战

事件绑定 除了使用addEventListener&#xff08;&#xff09;方法侦听特定元素上的事件外&#xff0c;还可以使用。on&#xff08;&#xff09;方法实现批元素的事件绑定。 event Type: String 需监听的事件名称&#xff0c;例如&#xff1a;‘tap’ selector Type: String 选择…

MySQL集群搭建——主从同步(一主二从)

一、安装MySQL数据库 Centos7安装MySQL5.7 目前准备了三台服务器作为主从配置数据库 #主 192.168.159.100:3306 #从 192.168.159.101:3306 #从 192.168.159.102:3306二、修改主数据库配置文件 vim /etc/my.cnf #在mysqld模块中添加如下配置信息 #开启二进制日志 log-binmast…

Win10家庭版利用Hyper-V虚拟机安装Kali Linux

目录 安装Hyper-V 批处理安装 重启电脑 下载Kali镜像 Kali官网下载 Hyper-V虚拟机 创建虚拟机 启动虚拟机 安装Kali 安装前配置 磁盘分区 系统安装 登录系统 近期学习网络安全的相关内容&#xff0c;需要用到很多的安全工具。偶然得知Kali Linux就是专门为网络安…