【排序】归并排序

归并排序

  • 动图演示:

在这里插入图片描述

  • 基本思想:分治思想

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

假设我们有左右两块有序区间的数组,可以对它直接进行合并。此时我们需要借助第三方数组,一次比较两块区间的起始位置,把小的那个放到新数组,随后依次比较,小的就放新数组,一直到结束。

但是现在存在一个问题,上述条件是假设了左半区间和右半区间有序,但是原先数组是无序的,也就是左半区间和右半区间均无序。怎么才能达到左半区间和右半区间有序最后再归并成整体有序呢?这就体现到了分治的思想了,将数组一直分,分到1个1个的,归并成有序变成2个2个的,然后归并成有序成4个4个的,最后再4个4个的归并成有序,最终至整体有序。

  • 画图解析其完整的归并过程:

在这里插入图片描述

这里我们先用代码实现其分解递归的过程,并用打印法表示其结果:

在这里插入图片描述

画图演示其部分递归分治的过程:

在这里插入图片描述

  • 总代码如下:
void _MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin >= end)
		return; //区间不存在就返回
	int mid = (begin + end) / 2;
	//[begin, mid] [mid+1, end]
	_MergeSort(a, begin, mid, tmp); //递归左半
	_MergeSort(a, mid + 1, end, tmp); //递归右半
 
	//归并[begin, mid] [mid+1, end]
	//printf("归并[%d,%d][%d,%d]\n", begin, mid, mid + 1, end);
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int index = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		//将较小的值放到tmp数组里头
		if (a[begin1] < a[begin2])
		{
			tmp[index++] = a[begin1++];
		}
		else
		{
			tmp[index++] = a[begin2++];
		}
	}
	//如若begin2先走完,把begin1后面的元素拷贝到新数组
	while (begin1 <= end1)
	{
		tmp[index++] = a[begin1++];
	}
	//如若begin1先走完,把begin2后面的元素拷贝到新数组
	while (begin2 <= end2)
	{
		tmp[index++] = a[begin2++];
	}
	//归并结束后,把tmp数组拷贝到原数组
	memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}
 
//归并排序
void MergeSort(int* a, int n)
{
	//malloc一块新数组
	int* tmp = (int*)malloc(sizeof(int) * n);
	assert(tmp);
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
}

归并排序非递归

  • 思想:

归并的非递归不需要借助栈,直接使用循环即可。递归版中我们是对数组进行划分成最小单位,这里非递归我们直接把它看成最小单位进行归并。我们可以通过控制间距gap来完成,先看图:

在这里插入图片描述

上述情况其实是在理想状态下可行的,只要数组长度不是2的次方倍都会出现问题,先简要看下理想状态下的伪代码,并用printf打印下归并过程:

在这里插入图片描述

再强调一遍,只要数组长度不是2的次方倍都会出现问题,像上述长度为8没有问题,那如若长度为6呢?

在这里插入图片描述

当长度为6不再是2的次方数时就运行出现问题了,综上我们需要考虑下极端情况:根据上述的区间范围,我们可以总结出以下三个可能会出现越界的情况:

  1. end1越界。
  2. begin2越界。
  3. end2越界。

1、end2越界:

在这里插入图片描述

2、begin2和end2均越界:

在这里插入图片描述

3、end1和begin2和end2均越界 :

在这里插入图片描述

综上,我们需要单独对这些极端情况处理。

//end1越界,修正即可
if (end1 >= n)
{
	end1 = n - 1;
}
//begin2越界,第二个区间不存在
if (begin2 >= n)
{
	begin2 = n;
	end2 = n - 1;
}
//begin2 ok,end2越界,修正下end2即可
if (begin2 < n && end2 >= n)
{
	end2 = n - 1;
}
  • 总代码如下:
//归并非递归
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	assert(tmp);
	int gap = 1;
	while (gap < n)
	{
		//分组归并,间距为gap是一组,两两归并
		for (int i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			//end1越界,修正即可
			if (end1 >= n)
			{
				end1 = n - 1;
			}
			//begin2越界,第二个区间不存在
			if (begin2 >= n)
			{
				begin2 = n;
				end2 = n - 1;
			}
			//begin2 ok,end2越界,修正下end2即可
			if (begin2 < n && end2 >= n)
			{
				end2 = n - 1;
			}
			printf("归并[%d,%d][%d,%d]\n", begin1, end1, begin2, end2);
			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				//将较小的值放到tmp数组里头
				if (a[begin1] < a[begin2])
				{
					tmp[index++] = a[begin1++];
				}
				else
				{
					tmp[index++] = a[begin2++];
				}
			}
			//如若begin2先走完,把begin1后面的元素拷贝到新数组
			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}
			//如若begin1先走完,把begin2后面的元素拷贝到新数组
			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}
		}
		memcpy(a, tmp, n * sizeof(int));
		gap *= 2;
	}
	free(tmp);
}

归并排序特性总结

1、归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。

2、时间复杂度:O(N*logN)。

3、空间复杂度:O(N)。

4、稳定性:稳定 。

内排序和外排序

在排序中,分为内排序和外排序,简单了解下其概念:

  • 内排序:数据量较少,在内存中进行排序。
  • 外排序:数据量很大,在磁盘上进行排序。

而我们前面学习的排序中,归并排序既可作为内排序,也可作为外排序,而其它几个排序只能是内排序,这也就说明了在处理数据量很大时,采用归并排序才能解决,其它排序不可。

如若我要排10亿个整数,就只能使用归并排序了,现在来简要算下其占比大小:

  • 1G = 1024MB
  • 1MB = 1024KB
  • 1KB = 1024Byte
  • 综上1G = 102410241024Byte,而10亿个整数40亿Byte,所以10亿个整数占4G

现在有10亿个整数(4G)的文件,只给你1G的运行内存,请对文件中的10亿个数进行排序。

核心思想: 数据量大,加载不到内存。想办法控制两个有序文件,两个有序文件归并成一个更大的有序文件。可以把这4G的文件分成4等份,每一份1G,分别读到内存进行归并排序,排完后再写回到磁盘小文件。

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

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

相关文章

【ES】--Elasticsearch的分词器深度研究

目录 一、问题描述及分析二、analyze分析器原理三、 multi-fields字段支持多场景搜索(如同时简繁体、拼音等)1、ts_match_analyzer配置分词2、ts_match_all_analyzer配置分词3、ts_match_1_analyzer配置分词4、ts_match_2_analyzer配置分词5、ts_match_3_analyzer配置分词6、ts…

2024年智能算法优化PID参数,ITAE、ISE、ITSE、IAE四种适应度函数随意切换,附MATLAB代码...

PID 参数整定就是确定比例系数&#xff08;Kp &#xff09;、积分系数&#xff08;Ki&#xff09;和微分系数&#xff08;Kd &#xff09;的过程&#xff0c;以便使 PID 控制器能够在系统中实现稳定、快速、准确的响应。 本期的主题 采用四种2024年的智能优化算法优化PID的三个…

《Java 简易速速上手小册》第6章:Java 并发编程(2024 最新版)

文章目录 6.1 线程的创建和管理 - 召唤你的士兵6.1.1 基础知识6.1.2 重点案例&#xff1a;实现一个简单的计数器6.1.3 拓展案例 1&#xff1a;定时器线程6.1.4 拓展案例 2&#xff1a;使用 Executor 框架管理线程 6.2 同步机制 - 维持军队的秩序6.2.1 基础知识6.2.2 重点案例&a…

pytorch花式索引提取topk的张量

文章目录 pytorch花式索引提取topk的张量问题设定代码实现索引方法gather方法验证 补充知识expand方法gather方法randint pytorch花式索引提取topk的张量 问题设定 或者说&#xff0c;有一个(bs, dim, L)的大张量&#xff0c;索引的index形状为(bs, X)&#xff0c;想得到一个(…

Java 基于 SpringBoot 的大药房管理系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

备战蓝桥杯---动态规划(入门1)

先补充一下背包问题&#xff1a; 于是&#xff0c;我们把每一组当成一个物品&#xff0c;f[k][v]表示前k组花费v的最大值。 转移方程还是max(f[k-1][v],f[k-1][v-c[i]]w[i]) 伪代码&#xff08;注意循环顺序&#xff09;&#xff1a; for 所有组&#xff1a; for vmax.....0…

使用Word Embedding+Keras进行自然语言处理NLP

目录 介绍&#xff1a; one-hot&#xff1a; pad_sequences: 建模: 介绍&#xff1a; Word Embedding是一种将单词表示为低维稠密向量的技术。它通过学习单词在文本中的上下文关系&#xff0c;将其映射到一个连续的向量空间中。在这个向量空间中&#xff0c;相似的单词在空间…

实现JNDI

实现JNDI 问题陈述 Smart Software Developer Ltd.想要开发一款Web应用程序,它使用servlt基于雇员ID显示雇员信息,雇员ID由用户通过HTML用户界面传递。雇员详细信息存储在Employee_Master表中。另外,Web应用程序应显示网站被访问的次数。 解决方案 要解决上述问题,需要执…

2024.2.6 模拟实现 RabbitMQ —— 数据库操作

目录 引言 选择数据库 环境配置 设计数据库表 实现流程 封装数据库操作 针对 DataBaseManager 单元测试 引言 硬盘保存分为两个部分 数据库&#xff1a;交换机&#xff08;Exchange&#xff09;、队列&#xff08;Queue&#xff09;、绑定&#xff08;Binding&#xff0…

腾讯云4核8G服务器够用吗?能支持多少人?

腾讯云4核8G服务器支持多少人在线访问&#xff1f;支持25人同时访问。实际上程序效率不同支持人数在线人数不同&#xff0c;公网带宽也是影响4核8G服务器并发数的一大因素&#xff0c;假设公网带宽太小&#xff0c;流量直接卡在入口&#xff0c;4核8G配置的CPU内存也会造成计算…

webpack面试解析

参考&#xff1a; 上一篇webpack相关的系列&#xff1a;webpack深入学习&#xff0c;搭建和优化react项目 爪哇教育字节面试官解析webpack-路白 1、Webpack中的module是什么&#xff1f; 通常来讲&#xff0c;一个 module 模块就是指一个文件中导出的内容&#xff0c;webpack…

全国计算机等级考试二级,MySQL数据库考试大纲(2023年版)

基本要求&#xff1a; 1.掌握数据库的基本概念和方法。 2.熟练掌握MySQL的安装与配置。 3.熟练掌握MySQL平台下使用&#xff33;&#xff31;&#xff2c;语言实现数据库的交互操作。 4.熟练掌握 MySQL的数据库编程。 5.熟悉 PHP 应用开发语言&#xff0c;初步具备利用该语言进…

Vue-自定义属性和插槽(五)

目录 自定义指令 基本语法 (全局&局部注册) 指令的值 练习&#xff1a;v-loading 指令封装 总结&#xff1a; 插槽&#xff08;slot&#xff09; 默认插槽 插槽 - 后备内容&#xff08;默认值&#xff09; 具名插槽 具名插槽基本语法: 具名插槽简化语法: 作…

RocksDB:高性能键值存储引擎初探

在现代的分布式系统和大数据应用中&#xff0c;一个高效、可靠的存储引擎是不可或缺的。RocksDB&#xff0c;由Facebook于2012年开发并随后开源&#xff0c;正是为了满足这类需求而诞生的。它是一个持久化的键值存储系统&#xff0c;特别适合在闪存&#xff08;Flash&#xff0…

AtCoder Beginner Contest 340 C - Divide and Divide【打表推公式】

原题链接&#xff1a;https://atcoder.jp/contests/abc340/tasks/abc340_c Time Limit: 2 sec / Memory Limit: 1024 MB Score: 300 points 问题陈述 黑板上写着一个整数 N。 高桥将重复下面的一系列操作&#xff0c;直到所有不小于2的整数都从黑板上移除&#xff1a; 选择…

SCI论文作图规范

SCI论文作图规范包括以下几个方面&#xff1a; 一、图片格式 SCI论文通常接受的图片格式包括TIFF、EPS和PDF等。其中&#xff0c;TIFF格式是一种高质量的图像格式&#xff0c;适用于需要高分辨率和颜色准确性的图片&#xff1b;EPS格式是一种矢量图形格式&#xff0c;适用于需…

Leetcode 1035 不相交的线

题意理解&#xff1a; 在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。 现在&#xff0c;可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线&#xff0c;这些直线需要同时满足满足&#xff1a; nums1[i] nums2[j]且绘制的直线不与任何其他连线&#xff…

Spring 事务

Spring 事务传播&#xff08;Propagation&#xff09;特性 REQUIRED 支持一个当前的事务&#xff0c;如果不存在创建一个新的。SUPPORTS 支持一个当前事务&#xff0c;如果不存在以非事务执行。MANDATORY 支持一个当前事务&#xff0c;如果不存在任何抛出异常。REQUIRES_NEW 创…

百面嵌入式专栏(面试题)驱动开发面试题汇总 2.0

沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们将介绍驱动开发面试题 。 1、Linux系统的组成部分? Linux内核、Linux文件系统、Linux shell、Linux应用程序。 2、Linux内核的组成部分? (1)第一种分类方式:内存管理子系统、进程管理子系统、文件管理子系…

react【六】 React-Router

文章目录 1、Router1.1 路由1.2 认识React-Router1.3 Link和NavLink1.4 Navigate1.5 Not Found页面配置1.6 路由的嵌套1.7 手动路由的跳转1.7.1 在函数式组件中使用hook1.7.2 在类组件中封装高阶组件 1.8 动态路由传递参数1.9 路由的配置文件以及懒加载 1、Router 1.1 路由 1.…
最新文章