【数据结构初阶】--- 归并排序

news2024/7/2 3:56:58

归并排序

    • 递归
      • 递归的思路
      • 归并的步骤:
      • 代码示例
    • 非递归
      • 快排为什么可以用栈模拟:
      • 归并可以用栈模拟吗?
      • 非递归的思路
      • 初版代码示例
      • 问题:越界
    • 时间复杂度
    • 针对递归的优化
      • 小区间优化

递归

递归的思路

  • 归并的前提是需要两个有序的区间,如何让整个区间变成两个有序的区间?
  • 那就利用递归的思路,将问题分成多个子问题去解决,
  • 想要有两个有序的区间,首先要有两个区间吧,那就从中间划分成两个区间,
  • 想让左区间有序,那就用归并,
  • 要归并就需要两个有序区间,那就将左区间从中间划分成两个区间,再想办法让这两个区间有序…以此类推,这个就是递归的思路

总结一下,想让一个区间有序,划分成左右区间,分别让两个区间有序,然后归并
结束条件:当这个要归并的区间只有一个值时结束

归并的步骤:

  1. 两个区间的边界:begin1,end1,begin2,end2
  2. 挨个比较两个区间的值,谁小就将谁放入临时数组中,直到一个区间拷贝完
  3. 此时另一个区间还有剩余,剩余的都比前面的值要大,所以就全部按顺序拷贝到临时数组中
  4. 将临时数组的值拷贝回原数组

代码示例

void _MergeSort(int* a, int left, int right, int* tmp)
{
	//当区间只有一个值时停止,不存在区间不存在的情况
	if (left == right)
		return;
	int mid = left + (right - left) / 2;

	//让左右两个区间都有序
	_MergeSort(a, left, mid,tmp);
	_MergeSort(a, mid + 1, right,tmp);

	//归并
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int begin = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] <= a[begin2])
		{
			tmp[begin++] = a[begin1++];
		}
		else
		{
			tmp[begin++] = a[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		tmp[begin++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[begin++] = a[begin2++];
	}
	//拷贝回原数组
	memcpy(a+left, tmp+left, sizeof(int)*(right - left + 1));
				
}

//归并排序
void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	_MergeSort(a, 0, n - 1, tmp);
	
}

非递归

快排为什么可以用栈模拟:

快排的非递归可以用栈模拟递归的过程,每次弹出区间的两个边界,从这个区间固定一个值的位置,再压入这个值的左右两个区间,这样的过程实际上模拟的是递归的前序遍历,先将本层的事做完,然后去遍历左右区间

归并可以用栈模拟吗?

不能的,因为归并排序是后序遍历的过程,先让左右区间各自有序,再进行归并操作。用栈模拟的过程中,试想一下,要让左右区间有序,所以先压右区间,再压左区间,然后要弹出栈顶元素,也就是左区间,再将这个区间的右区间、左区间压入栈中,以此类推,在过程中是没有机会进行归并操作的。

非递归的思路

在这里插入图片描述

  1. 一一归并,两两归并,四四归并…
  2. 归并一轮就把数据拷贝回原数组,再进行下一轮归并
  3. 用gap控制每轮归并一次的数据个数,初始化gap=1,每轮结束,gap=gap*2
  4. 直到gap>n时结束(一次归并的数据个数大于n时结束)
  5. begin1,end1,begin2,end2表示归并时数据的范围

初版代码示例

//归并非递归方法
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}

	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			int begin = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])
				{
					tmp[begin++] = a[begin1++];
				}
				else
				{
					tmp[begin++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[begin++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[begin++] = a[begin2++];
			}
		}
		memcpy(a, tmp, sizeof(int) * n);
		gap *= 2;
	}
}

问题:越界

上面的情况刚好对数据的个数为2的次方时有效,如果是9个数,10个数呢,
9个数时:在第一轮归并时就会越界在这里插入图片描述
10个数时:第二轮归并时越界
在这里插入图片描述
分析:
在这里插入图片描述
解决办法:

  1. 方法一:
    每次归并前判断
if(end1 > n || begin2 > n)
{
	break;
}
  • 如果是情况1、2,那就不用归并直接结束本轮归并,但是每轮结束后会将临时数组整体拷贝回原数组,那么没有归并的区间对应的临时数组那部分就是随机值,拷贝的时候就会用随机值覆盖原数组的这部分,为了避免覆盖,每次归并后就及时拷贝回数组,这样没归并的部分就不会被覆盖
  • 如果是情况3,还需要归并的情况就只需把end2修改成n-1
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");
		return;
	}

	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			int begin = i;
			
			//判断越界问题
			if (end1 > n || begin2 > n)
			{
				break;
			}
			if (end2 > n)
			{
				end2 = n - 1;
			}

			//归并
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])
				{
					tmp[begin++] = a[begin1++];
				}
				else
				{
					tmp[begin++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[begin++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[begin++] = a[begin2++];
			}
			//每次归并后及时拷贝回原数组
			memcpy(a+begin, tmp+begin, sizeof(int) * (end2-begin1+1));
		}
		gap *= 2;
	}
}
  1. 方法二:整体拷贝回原数组
    方法一是归并一次就拷贝一次,为的是避免未归并的区域会被覆盖
    想要整体拷贝还保证未归并的区域不会被覆盖,就需要将未归并的区域也先拷贝到临时数组中,然后一轮归并结束,整体拷贝回原数组
    根据上面的代码知道,想将数据拷贝到临时数组中就要进入归并的操作中,如果,归并的区间一个存在,一个不存在时,还能不能进入归并的操作呢,可以的,这种情况就类似,两个区间中一个区间已经拷贝完了,另一个区间还有一部分需要拷贝到临时数组。那么现在回到越界的区域,越界的区域本质就是不存在的范围,那我们将越界的范围修改成不存在的范围,再进行归并,就可以将这未越界的部分拷贝到临时数组中,最后一轮归并结束,就可以整体拷贝回原数组了
if(end1 > n)
{
	end1 = n - 1;
	begin2 = n; //将begin2和end2修改为不存在的区间[n,n-1]
	end2 = n - 1;
}
else if(begin2 > n)
{
	begin2 = n;
	end2 = n - 1;
}
else if(end2 > n)
{
	end2 = n - 1;
}

时间复杂度

由于每次都是将区间平分成两个区间,让左区间有序,让右区间有序,再进行归并,类似递归的过程类似二叉树的结构,因此,

  • 时间复杂度为:O(N*logN)
  • 空间复杂度:O(N)
    实际是O(N)+O(logN)
    • O(N)是开辟的临时数组的大小,
    • O(logN)是递归的高度,每层递归都需要建立栈帧

针对递归的优化

小区间优化

if(right - left + 1 < 10)
{
	InsertSort(a,left,right);
	return;
}

递归时,最后几层的递归调用次数占了整体的80%、90%,最后一层就占50%,所以要排的数的个数小于10个时,也就是最后三层,利用插入排序可以减少大量的递归次数,不过这种优化不会发生质的改变。

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

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

相关文章

LLM大模型算法学习资源持续整理

文章目录 waytoagiLLM101llm-coursellm-cookbook waytoagi 飞书文档写的AGI知识库。 https://www.waytoagi.com/ LLM101 karpathy更新中的大模型教程&#xff1a; https://github.com/karpathy/LLM101n llm-course Course to get into Large Language Models (LLMs) wi…

【前端】实现时钟网页

【前端】实现时钟网页 文章目录 【前端】实现时钟网页项目介绍代码效果图 项目介绍 时钟显示在网页中央&#xff0c;并且使网页能够切换白天和夜晚两种模式。搭建基本的html结构&#xff0c;动态得到实时的时&#xff0c;分&#xff0c;秒 通过Date()函数获得。将得到的数字根…

单片机学习记录

一&#xff0c;单片机及开发板介绍 1&#xff0c;基本介绍 单片机&#xff0c;英文Micro Controller Unit&#xff0c;简称MCU内部集成了CPU、RAM、ROM、定时器、中断系统、通讯接口等一系列电脑的常用硬件功能单片机的任务是信息采集(依靠传感器)、处理(依靠CPU)和硬件设备(…

SpringBoot整合拦截器和日期转换器

一、SpringBoot整合拦截器 1.添加拦截器 package com.by.interceptor;import com.by.pojo.User; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest; import java…

程序员如何用ChatGPT解决常见编程问题:实例解析

引言 在现代编程的世界中&#xff0c;技术进步日新月异&#xff0c;程序员们面临着各种各样的挑战和问题。解决这些问题的过程中&#xff0c;找到合适的工具至关重要。ChatGPT作为一种先进的人工智能语言模型&#xff0c;能够帮助程序员迅速、高效地解决常见的编程问题。本文将…

【Liunx-后端开发软件安装】Liunx安装FDFS并整合nginx

【Liunx-后端开发软件安装】Liunx安装nacos 文章中涉及的相关fdfs相关软件安装包请点击下载&#xff1a; https://download.csdn.net/download/weixin_49051190/89471122 一、简介 FastDFS是一个开源的轻量级分布式文件系统&#xff0c;它对文件进行管理&#xff0c;功能包括…

揭秘循环购模式:消费即投资,边消费边赚钱!

大家好&#xff0c;我是你们的电商顾问吴军。今天&#xff0c;我将带大家走进一个引人瞩目的商业模式——循环购模式。你可能会好奇&#xff0c;为何有商家能如此慷慨&#xff0c;消费1000元就送2000元&#xff1f;每天还有额外的现金收入&#xff1f;这背后究竟隐藏着怎样的秘…

Cadence计算器函数leafValue

与getData结合使用 leafValue( getData(“/output” ?result “dc”) 转自eetop https://bbs.eetop.cn/thread-931912-1-1.html

20240627 每日AI必读资讯

&#x1f50d;挑战英伟达&#xff01;00 后哈佛辍学小哥研发史上最快 AI 芯片 - 3名大学辍学生创立、目前仅35 名员工、刚筹集1.2 亿美元的团队&#xff1a;Etched。 - 史上最快Transformer芯片诞生了&#xff01; - 用Sohu跑Llama 70B&#xff0c;推理性能已超B200十倍&…

让AI保持怪异

让AI保持怪异 Anthropic的创意技术专家和员工设计师凯尔图尔曼(Kyle Turman)分享了一种深深引起共鸣的观点。他说(转述原话):“人工智能实际上真的很奇怪&#xff0c;我认为人们对这一点的认识还不够。”这引发了我向小组提出的问题:我们是否有消毒人工智能固有的陌生感的风险?…

办公软件汇总

1、OCR 1.1 pearOCR pearOCR 是一个免费的免费在线文字提取OCR工具网站。PearOCR界面简洁&#xff0c;所有过程均在网页端完成,无需下载任何软件&#xff0c;点开即用。官方地址&#xff1a;https://pearocr.com/ 参考&#xff1a;9款文字识别&#xff08;OCR&#xff09;工具…

魔众一物一码溯源防伪系统——守护品牌,守护信任!

在这个充满竞争的市场上&#xff0c;如何确保你的产品不被仿冒&#xff0c;如何赢得消费者的信任&#xff1f;魔众一物一码溯源防伪系统&#xff0c;为你提供一站式解决方案&#xff0c;守护你的品牌&#xff0c;守护消费者的信任&#xff01; &#x1f50d;魔众一物一码溯源防…

玩机进阶教程----MTK芯片使用Maui META修复基带 改写参数详细教程步骤解析

目前mtk芯片与高通芯片在主流机型 上使用比较普遍。但有时候版本更新或者误檫除分区等等原因会导致手机基带和串码丢失的故障。mtk芯片区别与高通。在早期mtk芯片中可以使用工具SN_Writer_Tool读写参数。但一些新版本机型兼容性不太好。今天使用另外一款工具来演示mtk芯片改写参…

企业如何通过数据资产入表与融资加速数字化转型

数据作为五大生产要素之一&#xff0c;是数字经济发展的基础。如何对数据资产进行确权、核算和变现&#xff0c;已成为数字经济时代的难点和热点。随着“数据资产入表”的提出与实践&#xff0c;这一领域迎来了新的变化与机遇。 一、什么是数据资产入表 在我国&#xff0c;数据…

【第2章】MyBatis-Plus代码生成器

文章目录 前言一、安装二、生成方式1.DefaultQuery (元数据查询)2.存在问题 三、快速生成1. 生成代码2. 目录结构 四、交互式总结 前言 全新的 MyBatis-Plus 代码生成器&#xff0c;通过 builder 模式可以快速生成你想要的代码&#xff0c;快速且优雅&#xff0c;跟随下面的代…

JetBrains Rider 2024安装教程

一、下载Rider 1、进入官网&#xff0c;点击“下载” 2、下载完毕 二、安装Rider 1、双击下载的exe文件 2、点击“下一步” 3、可以点击“浏览”选择安装路径&#xff0c;之后点击“下一步” 4、选中图中四项&#xff0c;点击“下一步” 5、选中图中四项&#xff0c;点击“下…

mysql自动填写当前时间,添加索引

mysql自动填写当前时间 在navicat操作界面创建表时&#xff0c;如果需要自动填写时间&#xff0c;可以操作如下 CURRENT_TIMESTAMP为表添加索引 ALTER table tableName ADD INDEX indexName(columnName)追加外键 ALTER TABLE tb_commentPhoto ADD CONSTRAINT FK_comment_ph…

什么是Arkose Labs挑战及其解决方法

Arkose Labs挑战是一种复杂的机制&#xff0c;旨在验证用户是真正的人类&#xff0c;而不是自动化的机器人或脚本。这一挑战在维护在线服务的安全性和完整性方面发挥着关键作用&#xff0c;通过防止欺诈活动并确保只有真实用户才能访问某些功能。 目录 什么是Arkose Labs挑战&a…

机械拆装-基于Unity-准备零件

目录 前言 1. 装配体模型的准备&#xff08;STEP格式保存为零件&#xff09; 1.1 关于不停提示“默认模板无效” 1.2 关于无法保存单个零件的解决 2. 整理装配体与零件 2.1 零件命名规则 2.2 建立子装配体 3. 装配体和零件转换格式 3.1 3DMax单位设置 3.2 装配体转换 3.3…

【JPCS独立出版】2024计算建模与应用数学国际学术会议暨中俄微分方程及其应用学术会议(CMAM 2024 DEA,8月2-4)

2024计算建模与应用数学国际学术会议暨中俄微分方程及其应用学术会议&#xff08;CMAM 2024 & DEA&#xff09;由大连海事大学理学院主办&#xff0c;上海海关学院、俄罗斯科学院科学城数学中心、辽宁省数学学会、大连市数学学会协办&#xff0c;AEIC学术交流中心承办。会议…