基础算法 第七课——归并排序

news2024/11/29 9:47:16

文章目录

  • 导言
  • 归并排序的概念
  • 步骤说明
  • 逐步分析
    • STEP1
    • STEP2
    • STEP3
    • STEP4
    • STEP5
    • STEP6
    • STEP0
  • 总结

导言

这,是一篇现学现卖的文章。因为,我根本没学过归并排序。所以,这篇文章,绝对能让您学懂归并。如果不懂,那我就再学一遍,再教一遍

归并排序的概念

从字面上分析,排序就是排序,归并就是归并。它们结合起来,就可以理解为用归并的方法来进行排序。

归并:
还是从字面上分析,归就想成是回归,并就想成是合并。为什么要合并呢?那肯定是已经分散的数据要合起来啊。那么,就很好理解了:归并就是将某些分散的数据合并起来。

知道归并的意思,就能够理解归并排序的意思了:先将一组无序的序列分散至独立的个体,再将已有序的子序列合并,得到完全有序的序列。

步骤说明

  1. 将已有数列一分为二,使其分解为左右两组。
  2. 重复执行步骤1,直至其分解成单独一个的个体。
  3. 定义两个指针,初始分别指向相邻两个子集的左端点。
  4. 比较当前两个指针所指数据大小,按照一定规律加入合并数列。
  5. 每次比较后,左指针右移一位,若左指针表示的数据大于右指针表示的数据,右指针右移一位。
  6. 重复执行步骤3、4、5,直至指针所指位置为空,即超出数据集范围。
  7. 重复执行3、4、5、6,直至合并为一组数列。

逐步分析

在这里插入图片描述
(图片源于网络)

STEP1

如图,我们先将数列分开来。分开数列很简单吧?我们就定义一个中间值,中间值以前的为左数据集,中间值以后的为右数据集。然后,就可以分别继续拆分,直到该数据集的左端点大于等于右端点。像这样:

void ms(int l,int r)//merge sort,l表示数据集左端点,r表示数据集右端点
{
	//SETP1
	if(l>=r)//不能再分了
	{
		return ;
	}
	
	int mid=(l+r)/2;//中间值
	
	ms(l,mid);//分别继续拆分
	ms(mid+1,r);
}

STEP2

既然已经分解完了,那么就该合并了。如何合并呢?我们可以定义一个数组 b b b 用来存放合并时的数字。按照步骤说明的方法,我们定义两个指针 i , j i,j i,j,一个指向左数据集,一个指向右数据集。但是,我们好像还需要一个指针 k k k,用来指向 b b b 数组的下标,以便存放数据。定义如下:

void ms(int l,int r)//merge sort,l表示数据集左端点,r表示数据集右端点
{
	//SETP1
	if(l>=r)//不能再分了
	{
		return ;
	}
	
	int mid=(l+r)/2;//中间值
	
	ms(l,mid);//分别继续拆分
	ms(mid+1,r);
	
	//STEP2
	int i=l,j=mid+1;//左右指针
	int k=l;//在l到r的数据中,b数组下标也是l到r
}

STEP3

最关键的一步来了。既然要合并,那么根据步骤说明,我们就需要比较 a [ i ] , a [ j ] a[i],a[j] a[i],a[j] 的大小。如果 a [ i ] < a [ j ] a[i]<a[j] a[i]<a[j] b [ k ] b[k] b[k] 就存 a [ i ] a[i] a[i],并且把 i , k i,k i,k 加一。如果 a [ i ] > a [ j ] a[i]>a[j] a[i]>a[j] b [ k ] b[k] b[k],就代表当前左指针已经不能再打了,再打的话就不能使最终数列有序。于是我们将 a [ j ] a[j] a[j] 存进 b [ k ] b[k] b[k],并且把 j , k j,k j,k加一。代码如下:

void ms(int l,int r)//merge sort,l表示数据集左端点,r表示数据集右端点
{
	//SETP1
	if(l>=r)//不能再分了
	{
		return ;
	}
	
	int mid=(l+r)/2;//中间值
	
	ms(l,mid);//分别继续拆分
	ms(mid+1,r);
	
	//STEP2
	int i=l,j=mid+1;//左右指针
	int k=l;//在l到r的数据中,b数组下标也是l到r
	
	//STEP3
	while(i<=mid&&j<=r)//只要指针没越界,就循环
	{
		if(a[i]<a[j])//按照分析进行
		{
			b[k]=a[i];
			i++,k++;
		}
		else
		{
			b[k]=a[j];
			j++,k++;
		}
	}
}

STEP4

上面的程序,有一个问题啊,那就是:循环的条件是同时满足指针 i , j i,j i,j 均没有越界。所以在循环结束后,如果指针 i , j i,j i,j 中,有一个还没有指到边界呢?那些剩下的值怎么办呢?很好办啊,我们直接将其接在数组后面就可以啦!为什么可以这样做?因为在循环结束后,剩下的值绝对是大于等于当前 b b b 数组的最后一位的。所以可以直接接在后面。代码:

void ms(int l,int r)//merge sort,l表示数据集左端点,r表示数据集右端点
{
	//SETP1
	if(l>=r)//不能再分了
	{
		return ;
	}
	
	int mid=(l+r)/2;//中间值
	
	ms(l,mid);//分别继续拆分
	ms(mid+1,r);
	
	//STEP2
	int i=l,j=mid+1;//左右指针
	int k=l;//在l到r的数据中,b数组下标也是l到r
	
	//STEP3
	while(i<=mid&&j<=r)//只要指针没越界,就循环
	{
		if(a[i]<a[j])//按照分析进行
		{
			b[k]=a[i];
			i++,k++;
		}
		else
		{
			b[k]=a[j];
			j++,k++;
		}
	}
	
	//STEP4
	while(i<=mid)//两种情况都有可能
	{
		b[k]=a[i];
		i++,k++;
	}
	while(j<=r)
	{
		b[k]=a[j];
		j++,k++;
	}
}

STEP5

相邻的左右两组数据集已经合并完啦!但是,它是存在 b b b 数组里的,所以我们还要将其复制给 a a a 数组:

void ms(int l,int r)//merge sort,l表示数据集左端点,r表示数据集右端点
{
	//SETP1
	if(l>=r)//不能再分了
	{
		return ;
	}
	
	int mid=(l+r)/2;//中间值
	
	ms(l,mid);//分别继续拆分
	ms(mid+1,r);
	
	//STEP2
	int i=l,j=mid+1;//左右指针
	int k=l;//在l到r的数据中,b数组下标也是l到r
	
	//STEP3
	while(i<=mid&&j<=r)//只要指针没越界,就循环
	{
		if(a[i]<a[j])//按照分析进行
		{
			b[k]=a[i];
			i++,k++;
		}
		else
		{
			b[k]=a[j];
			j++,k++;
		}
	}
	
	//STEP4
	while(i<=mid)//两种情况都有可能
	{
		b[k]=a[i];
		i++,k++;
	}
	while(j<=r)
	{
		b[k]=a[j];
		j++,k++;
	}
	
	//STEP5
	for(int I=l;I<=r;I++)
	{
		a[I]=b[I];
	}
}

STEP6

已经完成了,这一步是调用的步骤。如果您学懂了,那么应该知道,调用的步骤就为:ms(数组起始下标,数组终止下标);

STEP0

以上内容是从小到大排序的方法,如果从大到小,我们只需改一改判断。当然,归并排序还可以将两组数据排列成一组数据,对吧?下面,我将演示如何将两组数据按从小到大的顺序排成一组:

#include<bits/stdc++.h>

using namespace std;

int x[10000000],y[10000000];

int b[20000000];

void ms(int a[],int l,int r)//merge sort,l表示数据集左端点,r表示数据集右端点
{
	//SETP1
	if(l>=r)//不能再分了
	{
		return ;
	}
	
	int mid=(l+r)/2;//中间值
	
	ms(a,l,mid);//分别继续拆分
	ms(a,mid+1,r);
	
	//STEP2
	int i=l,j=mid+1;//左右指针
	int k=l;//在l到r的数据中,b数组下标也是l到r
	
	//STEP3
	while(i<=mid&&j<=r)//只要指针没越界,就循环
	{
		if(a[i]<a[j])//按照分析进行
		{
			b[k]=a[i];
			i++,k++;
		}
		else
		{
			b[k]=a[j];
			j++,k++;
		}
	}
	
	//STEP4
	while(i<=mid)//两种情况都有可能
	{
		b[k]=a[i];
		i++,k++;
	}
	while(j<=r)
	{
		b[k]=a[j];
		j++,k++;
	}
	
	//STEP5
	for(int I=l;I<=r;I++)
	{
		a[I]=b[I];
	}
}

int n,m;

int main()
{
	cin>>n>>m;//两组的数据个数
	for(int i=1;i<=n;i++)
	{
		cin>>x[i];
	}
	for(int i=1;i<=m;i++)
	{
		cin>>y[i];
	}
	
	ms(x,1,n);//排序
	ms(y,1,m);
	
	int i=1,j=1;//按照归并的方法,将两组数组看做左右数据集
	int k=1;
	while(i<=n&&j<=m)
	{
		if(x[i]<y[j])
		{
			b[k]=x[i];
			i++,k++;
		}
		else
		{				
			b[k]=y[j];
			j++,k++;
		}
	}
	while(i<=n)
	{
		b[k]=x[i];
		i++,k++;
	}	
	while(j<=m)
	{
		b[k]=y[j];
		j++,k++;
	}
	
	for(int i=1;i<=n+m;i++)//输出
	{
		cout<<b[i]<<" ";
	}
	
	return 0;
}

有什么比上面的代码更简单的吗,有!因为最终都是要排成一组,所以我们是不是可以在输入的时候直接输入在一起,再统一排序,然后输出呢?这样就大大减少了我们的码量。

总结

在开头说了,我也是才学归并。评价一下,归并排序其实也没有那么难,只要能理解归并是什么意思,在将其的排序步骤理清楚,就不难解出了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

KVM Forum 2022应该关注的话题

1. QEMU 和 KVM 自动性能基准测试 QEMU & KVM Automated Performance Benchmarking SUSE - Dario Faggioli, SUSE SUSE正在开发一个框架&#xff0c;用于对虚拟化工作负载进行自动性能基准测试。它是围绕着MMTests&#xff08;已经在Linux内核社区使用了几年&#xff09;建…

2022-Java 后端工程师面试指南 -(SSM)

前言 种一棵树最好的时间是十年前&#xff0c;其次是现在 Tips 面试指南系列&#xff0c;很多情况下不会去深挖细节&#xff0c;是小六六以被面试者的角色去回顾知识的一种方式&#xff0c;所以我默认大部分的东西&#xff0c;作为面试官的你&#xff0c;肯定是懂的。 上面的…

Mybatis之foreach

文章目录一、foreach属性二、使用foreach批量删除(法一)1.接口2.mapper文件3.测试类4.运行结果三、使用foreach批量删除(法二)1.mapper文件四、使用foreach批量插入1.接口2.mapper文件3.测试类4.运行结果一、foreach属性 collection&#xff1a;指定数组或者集合 item&#xf…

FPGA时序约束01——基本概念

前言1. 越来越多的时序问题 随着FPGA时钟频率加快与其实现的逻辑功能越来越复杂&#xff0c;开发者遇到的问题很多时候不再是代码逻辑的问题&#xff0c;而是时序问题。一些开发者可能有这样的经历&#xff0c;一个模块在100MHz时钟运行没问题&#xff0c;而将时钟频率改为150…

【仿牛客网笔记】 Spring Boot进阶,开发社区核心功能-事务管理

添加评论中会用到事务管理。 解决的程度不同&#xff0c;层级不同。我们一般选择中间的级别。 选择时既能满足业务的需要&#xff0c;又能保证业务的安全性&#xff0c;在这样的前提下我们追求一个更高的性能。 第一类丢失更新 图中是没有事务隔离的情况 第二类丢失更新 脏…

需求工程方法的学习

作业要求&#xff1a;总结尽可能多的需求工程的方法和技术&#xff0c;要求归纳总结各种方法的适用场景、优缺点等。说明&#xff1a;其中需求工程包括需求获取、需求分析、规格说明、验证、管理等。只要是用于需求工程相关的技术和方法都可以算。 软件需求工程划分为需求开发…

Linux 中 man手册中函数后面括号数字释义

文章目录简介参考资料简介 Linux手册页项目记录了用户空间程序使用的Linux内核和C库接口。 用man手册查看系统命令&#xff0c;系统调用&#xff0c;glibc函数时&#xff0c;会发现其后面会有个括号&#xff0c;括号里面是一个数字&#xff0c;比如&#xff1a; access(2), …

一文了解Spring框架

目录 SpringBoot VS Servlet Spring是什么&#xff1f; loC&#xff1a;控制反转 DI 创建一个Spring项目 创建一个Spring IOC容器 注册Bean对象 获取Bean对象 注意事项&#xff1a; 类注解 为什么有这么多类注解&#xff1f; 注册与注入 方法注解 Bean Spr…

《R语言数据分析》2022-2023第一学期课程分析报告

1 (30分)基本操作题 1.1 (10分) 请写出下面问题的R代码 1.(2分)安装并加载gtools扩展包。 install.packages(“gtools”) library(gtools) 2.(2分)查看当前已经加载的所有包。 as.data.frame(installed.packages())$Package 3.(2分)查看gtools包的帮助网页。 ?gtools…

《清单革命》内容梳理随笔

《清单革命》内容梳理&随笔 起 书即是将四散的知识按照逻辑和网状联系编排起来。你应该这样去读&#xff0c;高屋建瓴、层次有秩、显得貌似自己有经验&#xff08;褒义&#xff09;的读&#xff0c;读出一些感想和方法论&#xff0c;无论是读出书里的还是书外的&#xff…

【MySQL高级】SQL优化

5. SQL优化 5.1 大批量插入数据 环境准备 &#xff1a; CREATE TABLE tb_user_2 (id int(11) NOT NULL AUTO_INCREMENT,username varchar(45) NOT NULL,password varchar(96) NOT NULL,name varchar(45) NOT NULL,birthday datetime DEFAULT NULL,sex char(1) DEFAULT NULL,…

【数据库】实验五 数据库综合查询|多表查询、聚集函数、orderby、groupby

文章目录参考文章本文在实验四的基础上增加了orderby、聚集函数、groupby、多表查询的知识点&#xff0c;相较于上一次实验的难度变大了&#xff0c;嵌套表达更多了&#xff0c;逐渐开始套娃…… 其实可以看成一个偏正短语来拆分&#xff0c;再写成SQL语句&#xff0c;比如查询…

微信小程序|基于小程序实现人脸数量检测

一、文章前言二、具体流程及准备三、开发步骤四、完整代码一、文章前言 此文主要通过小程序实现检测图片中的人脸数量并标记出位置信息。 当近视的小伙伴看不清远处的人时&#xff0c;用小程序一键识别就可以在手机上看清楚啦&#xff0c;是不是很实用呢。 典型应用场景&#x…

2022年还在做手动测试?是该好好反思了

为什么会写这篇文章呢&#xff1f;主要是前段时间有个朋友在QQ上和我交流&#xff0c;说他干了10年的手工测试了&#xff0c;现在还能不能转行。 说实话&#xff0c;当时我听完非常惊讶&#xff01;由此&#xff0c;我写了今天这篇文章。内容纯属个人观点&#xff0c;如果对你…

STM32CubeMX学习笔记(46)——USB接口使用(HID自定义设备)

一、USB简介 USB&#xff08;Universal Serial BUS&#xff09;通用串行总线&#xff0c;是一个外部总线标准&#xff0c;用于规范电脑与外部设备的连接和通讯。是应用在 PC 领域的接口技术。USB 接口支持设备的即插即用和热插拔功能。USB 是在 1994 年底由英特尔、康柏、IBM、…

浅刷牛客链表题,逐步深入链表,理解链表

作者&#xff1a;渴望力量的土狗 博客主页&#xff1a;渴望力量的土狗的博客主页 专栏&#xff1a;手把手带你刷牛客 工欲善其事必先利其器&#xff0c;给大家介绍一款超牛的斩获大厂offer利器——牛客网 点击免费注册和我一起刷题吧 目录 1、反转链表 2、删除链表的倒数第n个…

RocketMQ 消息重新投递 解析——图解、源码级解析

&#x1f34a; Java学习&#xff1a;Java从入门到精通总结 &#x1f34a; 深入浅出RocketMQ设计思想&#xff1a;深入浅出RocketMQ设计思想 &#x1f34a; 绝对不一样的职场干货&#xff1a;大厂最佳实践经验指南 &#x1f4c6; 最近更新&#xff1a;2022年11月4日 &#x…

35、Java——一个案例学会Dao+service层对数据表的增删改查

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;乐趣国学的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;Java案例分…

deployment html--->JDBC--->mysql

spec: 相关属性定义 spec.selector: 符合该条件的收到该deployment管理 #spec.selector.matchLables 和 spec.template.metadata.labels 标签要一致 mysql Service metadata.name: Service的服务名 spec.ports: 虚拟端口 spec.selector: 哪些pod&#xff08;实列&…

《Java》深浅拷贝解析(还不会区分深浅拷贝吗?快进来)

目录 一、深浅拷贝的意义 浅拷贝 深拷贝 二、深浅拷贝举例 浅拷贝 深拷贝 一、深浅拷贝的意义 首先我们来了解一下深浅拷贝的意义 浅拷贝 浅拷贝是会将对象的每个属性进行依次复制&#xff0c;但是当对象的属性值是引用类型时&#xff0c;实质复制的是其引用&#xff0c…