【排序】归并排序(递归+非递归图示详解哦)

news2024/11/29 6:33:29

全文目录

  • 引言
  • 归并排序
    • 思路
    • 递归实现
  • 归排非递归
    • 思路
    • 实现
  • 总结

引言

在本篇文章中,将继续介绍一种排序算法:归并排序。
归并排序运用了归并的思想,即将两个有序数列归并为一个有序数列。在前面的合并两个有序链表时,运用了这种思想:戳我看归并实现合并两个有序链表(方法二)

归并排序

思路

归并排序需要一块与数组大小相同的空间,用于临时存储归并后的数据;

排序时,先将整个数组平分为两份,再分为4份……以此类推,直到不能再平分后(区间只剩一个元素);
向上归并,即将两个区间归并到临时空间中的对应位置;
再将临时数组中对应区间中已经排序好的数据拷回原数组;
一直向上归并,直到排序完成。
(需要注意的是,这里的平分不是同时进行的,在递归中,是一条线一条线进行的。左边递归到头后,返回给上一级,继续递归倒数第二层的右边,右边到底返回到上一级,执行最底层左右区间的归并。归并结束后返回到倒数第三层,然后递归该层的右边……)

在这里插入图片描述

递归实现

与快排的递归模式不同,快排是对最大的区间操作完成后,向下递归,对左右区间进行操作,直到排序结束;而归排是先递归到最小区间,再向上归并。所以在实现快排时,每层的操作在前,函数递归在后;而快排是递归在前,每层的操作在后;

函数有4个参数:原数组、区间的左右下标、临时空间。
初始传给函数的参数为整个数组,向下递归,当begin>=end时,return结束递归;
创建midi,记录区间的中间位置,再将左右区间分别递归;

之后实现对区间的操作:
首先用 begin1 、end1 、begin2 、end2记录要归并区间的左右区间的始末下标,并用k记录要归并的区间在临时空间temp中的对应起始位置;
然后将左右区间归并到temp的对应位置;
最后将temp对应空间中的数据拷贝回来:

在这里插入图片描述

对于归并的过程,即将两个区间中的数据依次比较,将较小的元素尾插到临时空间中:

在这里插入图片描述

void _MergeSort(int* a, int begin, int end, int* temp)
{
	if (begin >= end)
	{
		return;
	}
	int midi = (begin + end) / 2;
	_MergeSort(a, begin, midi, temp);
	_MergeSort(a, midi+1, end, temp);
	
	int begin1 = begin, end1 = midi;
	int begin2 = midi+1, end2 = end;
	int k = begin1;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			temp[k++] = a[begin1++];
		}
		else
		{
			temp[k++] = a[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		temp[k++] = a[begin1++];      
	}
	while (begin2 <= end2)
	{
		temp[k++] = a[begin2++];
	}

	memcpy(a + begin, temp + begin, sizeof(int)*(end - begin + 1));
}

void MergeSort(int* a, int n) //递归实现
{
	int left = 0;
	int right = n - 1;
	int* temp = (int*)malloc(sizeof(int) * n);
	if (temp == NULL)
	{
		perror("malloc");
		return;
	}
	_MergeSort(a, left, right, temp);
	free(temp);
}

归排非递归

思路

非递归实现归排时,与快排不同,不用记录上一层的区间,将某个区间归并后,转回原数组即可。所以我们只需要将数组分组,归并每两组的值,每组元素的数量从1开始,每次循环后,每组元素个数乘2,直到大于数组长度循环结束;

每层循环内,需要将这一层中,两个为一组的区间全部归并完成,并将这些数据转回原数组中:

(图)

实现

实现非递归时,首先动态开辟一块临时空间temp;
然后,创建gap为每层中每个需要归并区间的元素个数,并初始化为1;

外层while循环控制gap的值,大于等于数组的元素个数时,循环结束;
内层for循环控制每层中要归并的分组(两个元素个数为gap的小区间为一组归并):
左区间的起始位置begin1为i,结束位置end1为begin1+gap-1;右区间的起始位置begin2为end1+1,结束位置end2为begin2+gap-1;k为对应在temp中的起始位置下标;
然后需要判断的是,这组区间是否越界。由于for循环只是控制了i<n,即begin1<n,所以end1、begin2、end2都有可能越界;
当end1或begin2越界时,说明这组(左右)区间已经少了一个区间,不用归并了。当end2越界时,说明一组左右区间是存在的,需要归并,将end2改为数组最后一个元素的下标即可;

然后对左右区间进行归并,归并到temp中的对应位置;
每组(左右)区间归并后,就将数组拷回原数组:
【需要注意的是,由于之前的end2是可能越界的,所以不能直接拷贝2*gap个数据回去,而应该为end2-i+1个数据】

在这里插入图片描述

void MergeSortNonR2(int* a, int n) //非递归实现(分别转移)
{
	int gap = 1;
	int* temp = (int*)malloc(sizeof(int) * n);
	if (temp == NULL)
	{
		perror("malloc");
		return;
	}

	while (gap < n)
	{
		for (int i = 0; i < n; i += (gap * 2))//每层
		{
			int begin1 = i, end1 = begin1 + gap - 1;
			int begin2 = end1 + 1, end2 = begin2 + gap - 1;
			int k = begin1;

			//判断是否越界(如果越界,直接break跳过归并)
			if (end1 >= n || begin2 >= n)
			{
				break;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}

			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					temp[k++] = a[begin1++];
				}
				else
				{
					temp[k++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				temp[k++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				temp[k++] = a[begin2++];
			}

			memcpy(a + i, temp + i, (end2 - i + 1) * sizeof(int));//每次归并都转移
		}

		gap *= 2;//走向下一层
	}
}

总结

到此,关于归并排序的内容就已经介绍完了,同时排序算法的讲解也暂告一段落了
如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出

如果本文对你有帮助,希望一键三连哦

希望与大家共同进步哦

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

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

相关文章

四福来轮全向底盘实现写字功能

1. 功能说明 本文示例将实现R310b样机四福来轮全向底盘绘制“探索者”空心字的功能。 2. 电子硬件 本实验中采用了以下硬件&#xff1a; 主控板 Basra主控板&#xff08;兼容Arduino Uno&#xff09; 扩展板 Bigfish2.1扩展板 SH-ST步进电机扩展板电池11.1v动力电池 其它 步进…

干货分享:AI绘图学习心得-Midjourney绘画AI,让你的AI绘画之路少走弯路

干货分享&#xff1a;AI绘图学习心得-Midjourney绘画AI 最重要的Prompt和参数基本 Prompts高级Prompts 一、构图指令结构二、常用指令分享三、操作技巧总结四、常用风格词汇五、常用构图词汇六、高频实用词汇推荐&#xff1a;七、其他AI资料获取&#xff1a; 本篇没有什么长篇大…

01-Vue技术栈之基础篇(上)

目录 1、Vue简介1.1 Vue官网1.2 介绍与描述1.3 Vue 的特点1.4 与其它 JS 框架的关联1.5 Vue 周边库 2. 初识Vue2.1 Vue初体验2.2 注意事项2.3 js表达式和js代码&#xff08;语句&#xff09; 3、Vue模板语法3.1 语法分类3.2 插值语法3.3 指令语法 4、Vue模板语法4.1 数据绑定方…

Java中几种常量池面试总结

字符串常量池&#xff08;string pool&#xff09; 字符串常量池是JVM为了提升性能和减少内存消耗针对字符串&#xff08;String类&#xff09;专门开辟的一块区域&#xff0c;主要目的是为了避免字符串的重复创建。 当需要使用字符串时&#xff0c;先去字符串池中查看该字符…

使用vscode 创建vue3.0项目,应用element-plus框架

使用npm指令创建项目 npm init vite-app 项目名称 npm install npm run dev输入http://localhost:3000/ 查看 2、可自定义vue模板 输入vue.json 回车。复制下述代码&#xff0c;然后保存。 {"Print to console": {"prefix": "vue","b…

每天一道算法练习题--Day15 第一章 --算法专题 --- -----------二叉树的遍历

概述 二叉树作为一个基础的数据结构&#xff0c;遍历算法作为一个基础的算法&#xff0c;两者结合当然是经典的组合了。很多题目都会有 ta 的身影&#xff0c;有直接问二叉树的遍历的&#xff0c;有间接问的。比如要你找到树中满足条件的节点&#xff0c;就是间接考察树的遍历…

STM32物联网实战开发(3)——串口打印

串口打印 串口的使用在单片机开发过程中经常出现&#xff0c;因为他在显示数据和调试过程中特别的方便&#xff0c;使用起来也很简单。 1.用STM32CubeMx配置串口 串口1模式选择异步&#xff0c;不开启硬件控制流&#xff08;串口通信分为同步通信和异步通信&#xff0c;他们往…

云HIS : 电子病历模板制作过程技术经验分享

电子病历的制作就是按照医院机构的特色&#xff0c;根据不同业务需求&#xff0c;使用模板编辑与预览工具&#xff0c; 综合运用工具模块制作个性化、实用化、特色化电子病历模板的过程。 按照制作流程分为以下几个步骤&#xff1a; 1.明确病历类型&#xff1a;根据业务方向…

掌握好这几款TikTok数据分析工具,让你轻松提高曝光率!

为什么别人在TikTok发的普普通通的视频却有那么高的流量、几天内疯狂涨粉&#xff0c;而自己想破脑袋装饰自己的视频&#xff0c;结果却不如人意。 其实原因很简单&#xff0c;TikTok不像国内的抖音只面向中华民族&#xff0c;而是覆盖了150多个国家和75种语言用户&#xff0c…

【五一创作】Scratch资料袋

Scratch软件是免费的、免费的、免费的。任何需要花钱才能下载Scratch软件的全是骗子。 1、什么是Scratch Scratch是麻省理工学院的“终身幼儿园团队”开发的一种图形化编程工具。是面向青少年的一款模块化&#xff0c;积木化、可视化的编程语言。 什么是模块化、积木化&…

【VM服务管家】VM4.x算子SDK开发_3.1 环境配置类

目录 3.1.1 环境配置&#xff1a;CSharp算子SDK开发环境配置方法3.1.2 算子封装&#xff1a;使用C封装算子SDK的方法3.1.3 异常中断&#xff1a;算子SDK软件运行报错“托管调试助手”中断的解决方法3.1.4 深度学习&#xff1a;GPU运行深度学习算子引发StackOverFlow异常的方法 …

FP独立站推广成本太高?那是因为你没看到这篇!

近年来&#xff0c;越来越多的商家开始搭建自己的跨境电商独立站&#xff0c;做起了FP独立站。那么用独立站做FP到底有什么优势&#xff1f;还有&#xff0c;推广成本真的很高吗&#xff1f;今天这期就给大家扒一扒。 用独立站做FP的优势 1、塑造品牌&#xff0c;扩大经营触及…

【HarmonyOS】元服务WebView组件 H5使用localstorage

在日常开发中我们会在应用种接入H5网页&#xff0c;localStorage作为H5本地存储web storage特性的API之一&#xff0c;主要作用是将数据保存在客户端中。对于快速开发元服务&#xff0c;通过WebView组件运行H5如何使用localstorage呢&#xff1f;下文以API7 JavaUI为例为大家做…

k8s 集群搭建详细教程

参考&#xff1a; Kubernetes 文档 / 入门 / 生产环境 / 使用部署工具安装 Kubernetes / 使用 kubeadm 引导集群 / 安装 kubeadm B. 准备开始 一台兼容的 Linux 主机。Kubernetes 项目为基于 Debian 和 Red Hat 的 Linux 发行版以及一些不提供包管理器的发行版提供通用的指令每…

3.3 Linux shell命令(权限、输入输出)

目录 shell shell概述 shell分类 查看当前系统的shell 权限相关命令&#xff08;也是shell命令&#xff09; 基本命令 输入输出相关操作 输出命令 输入输出重定向 通配符 管道 历史查询、补齐功能 历史查询 自动补齐 命令置换 shell 什么是shell shell是一种负…

【VM服务管家】VM4.0软件使用_1.2 工具类

目录 1.2.1 文本保存&#xff1a;逐行保存格式化模块输出的方法1.2.2 脚本模块&#xff1a;循环模块搭配脚本使用的方法1.2.3 几何查找&#xff1a;彩色图像的几何查找方法1.2.4 深度学习&#xff1a;图像分割的面积的获取方法1.2.5 颜色识别&#xff1a;使用颜色识别工具做分类…

【Leetcode -86.分隔链表 -92.反转链表Ⅱ】

Leetcode Leetcode -86.分隔链表Leetcode -92.反转链表Ⅱ Leetcode -86.分隔链表 题目&#xff1a;给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每…

java数据结构之HashMap

目录 前言 1、初始化 1.1、初始化 1.2、插入第一条数据 2、数组 链表 2.1、插入数据&#xff1a;没有hash冲突 2.2、插入数据&#xff1a;Key不同&#xff0c;但产生hash冲突 2.3、插入数据&#xff1a;Key相同 3、数组 红黑树 3.1、链表如何转化为红黑树&#xff1f; 3.…

Postman测试实践笔记

Postman测试实践 文章目录 Postman测试实践一、Postman安装与使用1.1 Postman下载及安装1.1.2 Postman Mac版 1.2 Postman 更新1.2.1 mac 版更新 1.3 Postman 其他问题 二、网络相关知识2.1 接口2.1.1 软件为什么需要接口 2.2 接口测试2.2.1 什么是接口测试&#xff1a;2.2.2 为…

VTK下载并安装

去官网下载https://vtk.org/download/ 选择最新稳定版本 然后点击source后边的压缩包进行下载。 下载完成后将其解压到特定的文件夹下&#xff0c;然后打开cmake-gui.exe&#xff0c;第一行选择刚刚解压的文件夹&#xff0c;这个文件夹下有一个CMakeLists.txt文件&#xff0c…