归并排序的实现(递归与非递归)

概念

基本思想:归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有
序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:

归并排序的递归实现

原理

实现

比较begin1和begin2的值的大小,小的插到tmp数组后面,代码实现如下:

	//归并	
	int begin1 = begin, end1 = mid;
	int begin2 = mid +1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}
	//只有begin1或begin2其中一个结束了才会进入后面的循环.

	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}

	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}
	memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}

了解归并排序的单趟过程后,再对其进行递归,即可实现归并排序,代码如下:

//处理一段区间
void _MergeSort(int* a, int* tmp, int begin, int end)
{
	if (begin = end)	
		return;

	int mid = (begin + end) / 2;
	//将区间分为[begin, mid] [mid + 1, end]两个区间,若两区间都有序即可
	_MergeSort(a, tmp, begin, mid);
	_MergeSort(a, tmp, mid + 1, end);

	//归并	
	int begin1 = begin, end1 = mid;
	int begin2 = mid +1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}
	//只有begin1或begin2其中一个结束了才会进入后面的循环.

	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}
	
	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}
	memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail!");
	}
	_MergeSort(a, tmp, 0, n - 1);

	free(tmp);
	tmp = NULL;
}
递归的理解 

假设现在我们排0到9这10个数据,则我们先走左边(begin到mid)0到4,要想让0到4有序,则先走左边(0到2),要想让0到2有序,则继续递归分为0到1,再继续分为0到0,1到1,即左边分完,再继续向上再继续递归右边(3到4),再往上走,类似于二叉树中的后 序遍历. 递归展开图:

时间复杂度

空间复杂度也类似于堆排序,每一层有N个,有logN层,则时间复杂度为O(logN).

归并排序的非递归实现

 对于归并排序的非递归实现,我们需要转换思路,将数据一一归并,再两两归并,再四四归并......依此类推:

非递归的本质其实是循环,我们设一个gap为每组归并数据的个数,如上图,第一排gap为1,第二排为2,第三排为4,依此类推, 首先将数据两两配对,再四四配对,代码实现如下:

void MergeSortNonR(int* a, int n)
{

	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail!");
	}
	//gap为每组归并数据的数据个数
	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)//i代表了每次归并的起始位置
		{
			//[begin1, end1] [b	egin2, end2]
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[i++] = a[begin1++];
				}
				else
				{
					tmp[i++] = a[begin2++];
				}
			}
			//只有begin1或begin2其中一个结束了才会进入后面的循环.

			while (begin1 <= end1)
			{
				tmp[i++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[i++] = a[begin2++];
			}
			memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));//每次归并后都拷贝回去	
		}
		gap *= 2;	

	}
	//归并一次归两组
	free(tmp);
	tmp = NULL;
}

但如图所示,我们只能排列2的指数次方数的数据,因为不为2的偶数次方数的数据会导致数据溢出,在控制台显示如下: 

一共10个数据,如果显示大于9的数据都是溢出的数据,而数据溢出分为三种情况

第一种与第二种合并为第一种

1.右边数组全部溢出

这种代表右边已经是大数,我们不用管,直接尾插入新数组即可.

if (begin2 >= n)
{
	break;
}

2.右边数组未全部溢出

这种情况我们要对数组进行调整处理(修正):将end2减一即可

if (end2 >= n)
{
	end2 = n - 1;
}

这样,我们的总代码即为:

总代码

void MergeSortNonR(int* a, int n)
{

	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail!");
	}
	//gap为每组归并数据的数据个数
	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)//i代表了每次归并的起始位置
		{
			//[begin1, end1] [b	egin2, end2]
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			//第二组不存在(都越界),则不需要归并
			int j = i;
			if (begin2 >= n)
			{
				break;
			}
			//第二组的begin2没有越界,则需要修正,并继续归并
			if (end2 >= n)
			{
				end2 = n - 1;
			}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[i++] = a[begin1++];
				}
				else
				{
					tmp[i++] = a[begin2++];
				}
			}
			//只有begin1或begin2其中一个结束了才会进入后面的循环.

			while (begin1 <= end1)
			{
				tmp[i++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[i++] = a[begin2++];
			}
			memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));//每次归并后都拷贝回去	
		}
		gap *= 2;	

	}
	//归并一次归两组
	free(tmp);
	tmp = NULL;
}

 

 

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

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

相关文章

一篇文章搞懂qt图形视图框架setRect和setPos函数的意义

setRect()函数 三个坐标系我就不多说了&#xff0c;view原点默认在左上角&#xff0c;scene和item的原点默认都在中心位置。 注意&#xff1a;此函数并不能设置一个item的位置&#xff0c;我的理解是当一个item调用该函数时&#xff0c;会构建一个一个item的坐标系&#xff0c…

秋招提前批面试经验分享(下)

⭐️感谢点开文章&#x1f44b;&#xff0c;欢迎来到我的微信公众号&#xff01;我是恒心&#x1f60a; 一位热爱技术分享的博主。如果觉得本文能帮到您&#xff0c;劳烦点个赞、在看支持一下哈&#x1f44d;&#xff01; ⭐️我叫恒心&#xff0c;一名喜欢书写博客的研究生在读…

vue3+antd 实现点击按钮弹出对话框

格式1&#xff1a;确认对话框 按钮&#xff1a; 点击按钮之后&#xff1a; 完整代码&#xff1a; <template><div><a-button click"showConfirm">Confirm</a-button></div> </template> <script setup> import {Mod…

关于Web开发的详细介绍

目录 一、什么是Web&#xff1f; 二、Web网站的工作流程和开发模式 &#xff08;1&#xff09;简单介绍 &#xff08;2&#xff09;工作流程 1、第一步 2、第二步 &#xff08;3&#xff09;Web网站的开发模式 1、前后端分离开发模式 ​编辑2、混合开发模式 三、开发W…

DPDK源码分析之(1)libmbuf模块

DPDK源码分析之(1)libmbuf模块 Author&#xff1a;OnceDay Date&#xff1a;2024年7月2日 漫漫长路&#xff0c;有人对你笑过嘛… 全系列文档可参考专栏&#xff1a;源码分析_Once-Day的博客-CSDN博客 参考文档&#xff1a; DPDK downloadGetting Started Guide for Linux…

业界数据架构的演变

目录 一、概述 二、业务处理-单体架构 三、业务处理-微服务架构 四、数据分析-大数据Lambda架构 五、数据分析-Kappa架构 六、数据分析-LambdaKappa混合架构 七、湖仓一体架构 一、概述 近年来随着越来越多的大数据技术被开源&#xff0c;例如&#xff1a;HDFS、Spark等…

最小表示法

#define _CRT_SECURE_NO_WARNINGS #include<bits/stdc.h> using namespace std;const int N (int)3e5 5; int n; int a[N * 2];int main() {cin >> n;for (int i 0; i < n; i) {cin >> a[i];a[i n] a[i]; // 构造成链}int l 0, r 1; // 一开始 r …

进入防火墙Web管理页面(eNSP USG6000V)和管理员模块

1、进入防火墙Web管理页面 USG系列是华为提供的一款高端防火墙产品&#xff0c;其特点在于提供强大的安全防护能力和灵活的扩展性。 以eNSP中的USG6000为例&#xff1a; MGMT口&#xff08;web管理口&#xff09;&#xff1a;对应设备上的G0/0/0口&#xff0c;上面初始配有一…

算法-常见数据结构设计

文章目录 1. 带有setAll功能的哈希表2. LRU缓存结构3. O(1)时间插入删除随机(去重)4. O(1)时间插入删除随机(不去重)5. 快速获取数据流中的中位数6. 最大频率栈7. 全O(1)结构8. LFU缓存结构 本节的内容比较难, 大多是leetcodeHard难度级别的题目 1. 带有setAll功能的哈希表 哈希…

QCustomPlot+ vs2022+ qt

零、printSupport 步骤一&#xff1a;下载QCustomPlot 访问QCustomPlot的官网 QCustomPlot 下载最新版本的源代码。 步骤二&#xff1a;配置项目 创建新的Qt项目&#xff1a; 打开VS2022&#xff0c;创建一个新的Qt Widgets Application项目。 将QCustomPlot源代码添加到项目…

集合复习(java)

文章目录 Collection 接口Collection结构图Collection接口中的方法Iterator 与 Iterable 接口Collection集合遍历方式迭代器遍历增强 for 遍历 List&#xff08;线性表&#xff09;List特有方法ArrayList&#xff08;可变数组&#xff09;ArrayList 底层原理ArrayList 底层原理…

【UML用户指南】-30-对体系结构建模-模式和框架

目录 1、机制 2、框架 3、常用建模技术 3.1、对设计模式建模 3.2、对体系结构模式建模 用模式来详述形成系统体系结构的机制和框架。通过清晰地标识模式的槽、标签、按钮和刻度盘 在UML中&#xff0c; 对设计模式&#xff08;也叫做机制&#xff09;建模&#xff0c;将它…

前端技术(三)—— javasctipt 介绍:jQuery方法和点击事件介绍(补充)

6. 常用方法 ● addClass() 为jQuery对象添加一个或多个class <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">&…

Efficient Contrastive Learning for Fast and Accurate Inference on Graphs

发表于:ICML24 推荐指数: #paper/⭐⭐⭐ 创新点一颗星,证明三颗星(证明的不错,值得借鉴,但是思路只能说还行吧) 如图, 本文采取的创新点就是MLP用原始节点,GCN用邻居节点的对比学习.这样,可以加快运算速度 L E C L − 1 ∣ V ∣ ∑ v ∈ V 1 ∣ N ( v ) ∣ ∑ u ∈ N ( v )…

汇聚荣拼多多电商的技巧有哪些?

在电商平台上&#xff0c;汇聚荣拼多多以其独特的商业模式和创新的营销策略吸引了大量消费者。那么&#xff0c;如何在这样一个竞争激烈的平台上脱颖而出&#xff0c;成为销售佼佼者呢?本文将深入探讨汇聚荣拼多多电商的成功技巧。 一、精准定位目标客户群体 首先&#xff0c;…

Android增量更新----java版

一、背景 开发过程中&#xff0c;随着apk包越来越大&#xff0c;全量更新会使得耗时&#xff0c;同时浪费流量&#xff0c;为了节省时间&#xff0c;使用增量更新解决。网上很多文章都不是很清楚&#xff0c;没有手把手教学&#xff0c;使得很多初学者&#xff0c;摸不着头脑&a…

【Threejs进阶教程-优化篇】4.Vue/React与threejs如何解决冲突和卡顿(续)

Vue/React与threejs如何解决冲突和卡顿-续 使用说明核心思路环境搭建(vuethree)vue运行机制分析业务分离使用threejs做背景 3D模块封装使用ES6的Class来让逻辑性更强Threejs尽量按需引入创建一个类扩展写法本次代码执行顺序 扩展内容添加orbitControls和辅助线解决事件覆盖 与V…

MUX VLAN实现二层流量的弹性管控

一、模拟场景&#xff0c;企业有一台服务器&#xff0c;部门A&#xff0c;部门B&#xff0c;访客 二、要求&#xff1a;三者都可以访问服务器&#xff0c;部门A和B可以进行部门内部通信&#xff0c;A和B不可以通信&#xff0c;访客只能访问服务器 三、拓扑如下图 四、配置流程…

UE5 05-利用 timeline 插值运动

理解成 unity Dotween DoMove 插值运动即可 AddTimeLine 节点 物体插值运动 物体插值缩放 一个timeline 可以K多个动画帧

【js基础巩固】深入理解作用域与作用域链

作用域链 先看一段代码&#xff0c;下面代码输出的结果是什么&#xff1f; function bar() {console.log(myName) } function foo() {var myName "极客邦"bar() } var myName "极客时间" foo()当执行到 console.log(myName) 这句代码的时候&#xff0c…