【八大排序(八)】归并排序高阶篇-非递归版

news2024/7/2 9:56:50

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:八大排序专栏⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学习排序知识
  🔝🔝


在这里插入图片描述

归并非递归版

  • 1. 前情回顾
  • 2. 归并非递归基本思路
  • 3. 对于循环的(大/小)框架的思考
  • 4. 归并排序非递归代码实现
  • 5. 特殊情况下对代码的优化
  • 6. 总结以及拓展

1. 前情回顾

归并排序是一个全新的排序
它不是对任意排序的优化和改进
它自成一派,并且效率非常可观
要想掌握归并的非递归版本
就要先理解归并递归版实现
详情可以跳转:归并初阶篇

掌握了非递归版将是面试时
你和别人拉开差距的重要一环
大学生特种兵,开卷!

在这里插入图片描述


2. 归并非递归基本思路

我们先定义一个无序数组:

int a[]={10,6,7,1,3,9,4,2};

对于当前数组.

我们需要做的是:

  1. 第一次循环:

将10和6这一组,7和1这一组
3和9这一组,4和2这一组归并排序
使这四组的各两个数变为有序

  1. 第二次循环:

6.10和1.7一组,3.9和2.4一组
进行归并排序,使这两组的各
四个数字都变为有序

  1. 第三次循环:

1.6.7.10和2.3.4.9一组
进行归并排序,使数组整体有序

画图理解:

在这里插入图片描述


3. 对于循环的(大/小)框架的思考

对于大框架的思考:

先来找找规律:

  • 八个元素需要归并三次
  • 四个元素需要归并两次
  • 十六个元素需要归并四次

归并循环的次数K和数组元素个数n
的关系是:

2 ^ K = n

所以我们可以这样设计最外层循环:

int gap=1;
while(gap<n)
{
	//...
	gap* = 2;
}

对于小框架的思考:

  1. 循环第一次

区间 [0,0] 和区间 [1,1] 归并
区间 [2,2] 和区间 [3,3] 归并

  1. 循环第二次

区间 [0,1] 和区间 [2,3]归并
区间 [4,5] 和区间 [6,7]归并

  1. 循环第三次

区间 [0,3] 和区间 [4,7] 归并
归并结束,数组整体有序.

我们根据画图中的gap来思考:
gap从1开始,每归并一次便扩大两倍

每次循环的区间可以这样定义:

A组: [ i , i + gap - 1]
B组: [ i + gap , i + 2*gap - 1]

所以我们可以这样设计内层循环:

for(int i=0;i<n;i+=2*gap)
{
	//...
}

4. 归并排序非递归代码实现

有了前面做铺垫,直接上硬菜:

//归并排序(非递归)偶数没问题,奇数还需要改正
void MergeSortNonRE(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		printf("动态开辟失败");
		exit(-1);
	}
	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i = i + 2 * gap)
		{
			int begin1 = i;
			int end1 = i + gap - 1;
			int begin2 = i + gap;
			int end2 = i + 2 * gap - 1;
			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[index++] = a[begin1++];
				}
				else
				{
					tmp[index++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}
		}
		for (int i = 0; i < n; i++)
		{
			a[i] = tmp[i];
		}
		gap = gap * 2;
	}

	free(tmp);
	tmp = NULL;
}

注意:只有大小框架的设计是本节内容
归并排序具体实现(也就是后面的代码)
具体可以参考:归并排序初阶篇


5. 特殊情况下对代码的优化

  1. 对元素个数为偶数的思考:
  • 上面的情况用到的用例的
    数组元素个数都是偶数个
    所以可以两两匹配,归并不会出错

先定义一个奇数个元素的数组:

int a[]={10,6,7,1,3,9,4,2,5};
  1. 对元素个数为奇数的思考:
  • 而当数组元素个数是奇数个时
    归并完4.2后.归并5和5后面的元素

  • 然而5后面的元素不存在,或者说越界了
    系统就会报错,因为有越界操作存在
    这里需要优化代码解决这个问题

  1. 优化后的代码:
//归并排序(非递归完全版)
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		printf("动态开辟失败");
		exit(-1);
	}
	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i = i + 2 * gap)
		{
			int begin1 = i;
			int end1 = i + gap - 1;
			int begin2 = i + gap;
			int end2 = i + 2 * gap - 1;
			
			//处理特殊的越界情况
			// end1 越界,[begin2,end2]不存在
			if (end1 >= n)
			{
				end1 = n - 1;
			}

			//[begin1,end1]存在 [begin2,end2]不存在
			if (begin2 >= n)
			{
				begin2 = n;
				end2 = n - 1;
			}

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

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

	free(tmp);
	tmp = NULL;
}

6. 总结以及拓展

关于递归排序所有内容已经结束了
完结撒花!

  • 如果你面试刚好被问到归并排序
    而你又刚好掌握了归并的递归和非递归
    这时面试官一定会更倾向于你.
    而不是旁边写一行代码报3个错的竞争者

在这里插入图片描述

拓展:

归并排序用途:

除了可以用来排序之外
还可以求逆序对数

在归并的过程中计算每个小区间的逆序对数
进而计算出大区间的逆序对数
(也可以用树状数组来求解)


🔎 下期预告:计数排序 🔍

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

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

相关文章

JVM的内容

0、Java基础考点 1、谈谈你对Java的理解 平台无关性(一次编译&#xff0c;到处运行)GC(垃圾清理)语言特性(泛型、反射)面向对象(封装、继承、多态)类库异常处理 2、Java是如何实现平台无关性的(一处编译&#xff0c;到处运行) 编译时&#xff08;语法和语义进行检测&#xf…

0012-TIPS-pawnyable : Use-After-Free

原文 Linux Kernel PWN | 040203 Pawnyable之UAF https://pawnyable.cafe/linux-kernel/LK01/use_after_free.html 题目下载 漏洞代码 #include <linux/module.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> #includ…

【踩坑】Windows11安装WSL2,然后装miniconda

Windows11安装WSL2 跟着官方文档一步步来就完事了&#xff0c;你要在vscode上用还是用docker都有教程微软WSL文档 遇到问题 Installing, this may take a few minutes… WslRegisterDistribution failed with error: 0x80370102 Please enable the Virtual Machine Platform W…

chatgpt赋能python:Python收费怎么办?

Python收费怎么办&#xff1f; Python是一门非常流行的编程语言&#xff0c;特别是在数据科学和机器学习领域中。许多人使用Python来编写自己的应用程序和脚本&#xff0c;但是有些人会对Python的收费问题感到困惑。本文将介绍Python的收费情况以及如何解决这个问题。 Python…

Shell - 02_shell的自定义变量

一、shell的自定义变量 1.定义变量&#xff1a;变量名变量值 如&#xff1a;num10 2.引用变量&#xff1a;$变量名 如&#xff1a;i$num 把变量 num 的值付给变量 i 3.显示变量&#xff1a;使用 echo 命令可以显示单个变量取值 如&#xff1a;echo $num 4.清除变量&…

高校学生公寓安全用电物联网平台的应用

摘要:本文针对高校学生公寓用电特点,从安全用电角度提出了一套集用电管理、计量、恶性负载智能识别控制、实时跟踪检测等功能于一体的数字化安全用电管理系统技术解决方案———学生公寓智能控电管理系统。 关键词:公寓恶性负载安全用电智能系统 0、引言 近年来,为了响应国家…

webpack编译打包

1.安装webpack npm install webpack webpack-cli --save-dev 2.添加命令 在package.json文件中添加启动命令 3.打包 webpack.config.js文件 通过配置文件构建&#xff1a;npx webpack --config webpack.config.js 4.文件结构 src:用于存放代码&#xff0c;一般入口为index.…

HQChart实战教程64-自定义分时图标题栏

HQChart实战教程64-自定义分时图标题栏 分时图标题栏步骤1. 替换分时图标题格式化输出函数2. 格式化输出函数说明HQChart插件源码地址完整的demo源码分时图标题栏 分时图标题栏显示的是当前十字光标所在位置的分钟信息,显示在分时图窗口顶部。一般会显示品种的名称,日期,时间…

android内部存储和外部存储

我们在开发Android应用的过程中&#xff0c;避免不了要用到数据持久化技术&#xff0c;所谓的数据持久化就是将RAM中的瞬时数据保存到ROM中&#xff0c;保证在App退出或者手机关机后数据不会丢失。我们常用的数据持久化的方式有文件存储&#xff0c;数据库存储&#xff0c;Shar…

动手实现条件随机场(下)

引言 本文基于PyTorch实现条件随机场&#xff0c;实现CRF层参考论文Neural Architectures for Named Entity Recognition中关于CRF层的描述。包含大量的图解和例子说明&#xff0c;看完一定能理解&#xff01; 论文地址&#xff1a; https://arxiv.org/pdf/1603.01360.pdf 也可…

chatgpt赋能python:Python搜索算法:如何提高你的搜索体验

Python 搜索算法&#xff1a;如何提高你的搜索体验 在当今信息爆炸的时代&#xff0c;搜索已成为许多人获取信息的主要途径。而 Python 的搜索算法&#xff0c;也在此背景下日渐受到重视。本篇文章将深入探讨 Python 搜索算法&#xff0c;介绍以及如何使用它来提高你的搜索体验…

SpringBoot整合模板引擎Thymeleaf(5)

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 概述 在本节教程中&#xff0c;我们在之前案例的基础上详细介绍利用Thymeleaf实现国际化。 项目结构 依赖文件 请在pom.xml文件中添加如下依赖。 <?xml version"…

Android 9 蓝牙协议初始化

先讲一下Application类的使用 要使用自定义的Application&#xff0c;首先就是要自己新建一个Application的子类&#xff0c;然后把它的名字写在manifest文件里面的application标签里的android:name属性就行&#xff0c;如我的Application子类名字是BaseApplication&#xff0c…

java入门3(程序流程结构)

目录 大部分和C语言一样 顺序结构 选择结构 简单if语句 ​ 多重if结构 嵌套if结构 Switch选择结构 if和Switch嵌套 循环结构 while循环 do while 语句 for循环 break和continue break 中断指令&#xff0c;结束所在层的循环 continue:中断指令 中断本轮的循环&…

chatgpt赋能python:Python收发短信:简单可靠的解决方案

Python收发短信&#xff1a;简单可靠的解决方案 如果您需要向客户发送定期提醒或通知的短信&#xff0c;则 Python 是一种简单易用的解决方案。在本文中&#xff0c;我们将介绍如何使用 Python 发送和接收短信&#xff0c;并探讨一些流行的短信 API。 什么是短信 API&#xf…

深入分析vfio-user设备实现原理 —— Client侧

文章目录 前言数据结构protocolVFIOUserHdrvfio_user_commandVFIOUserHdr flags 设备模型VFIODeviceVFIODevIOVFIOContIO VFIOPCIDeviceVFIOKernPCIDeviceVFIOUserPCIDevice proxyVFIOUserMsgVFIOProxy 流程详解消息发松流程DMA映射流程 前言 数据结构 protocol VFIO User P…

「解析」YOLOv4模型小结

Paper Yolo v4: https://arxiv.org/abs/2004.10934 Scaled-YOLOv4: Scaling Cross Stage Partial Network Source code:https://github.com/AlexeyAB/darknet Bag of Freebies(BoF) 只增加训练成本&#xff0c;但是能显著提高精度&#xff0c;并不影响推理速度&#xff1b;数据…

Kubernetes使用Istio

Kubernetes使用Istio 1、基本概念 1.1、流量方向 南北流量&#xff08;NORTH-SOURTH-TRAFFIC&#xff09;&#xff1a;客户端到服务器之间通信的流量 东西流量(EAST-WEST-TRAFFIC)&#xff1a;指的是服务器和服务器之间的流量 1.2、Service Mesh 2、安装Istio 2.1、下载 …

【编译、链接、装载九】静态链接

【编译和链接九】静态链接 一、demo二、空间与地址分配1、相似段合并 三、即虚拟地址VMA&#xff08;Virtual Memory Address&#xff09;四、重定位1、add调用2、printf调用——同add2、shared 五、重定位表六、符号解析七、c相关问题1、重复代码消除2、全局构造与析构3、C与A…

从创建到维护:掌握package.json的最佳实践

文章目录 I. 介绍什么是package.jsonpackage.json的作用npm与package.json的关系 II. 创建package.jsonnpm init自动生成package.jsonpackage.json各个字段的含义 III. dependencies和devDependenciesdependencies和devDependencies的区别安装依赖包安装依赖包的版本更新依赖包…