数据结构---递归转化为非递归

news2024/12/28 20:19:39

递归转化为非递归

  • 前言
  • 快速排序非递归
  • 归并排序的非递归

前言

为什么要学习非递归写法呢?
当我们在用递归实现一个程序的时候,要考虑一个问题,这个程序用递归去实现,当数据量庞大的时候,会不会造成栈溢出(STACK OVERFLOW)呢?
如果没有造成还好,造成了怎么解决这个问题呢?这个时候就需要用到非递归,把一个递归实现的程序转化为非递归,是一个程序猿的基本功。

int Sum(int n)
{
	if (n == 1)
	{
		return 1;
	}
	return n + Sum(n - 1);
}

int main()
{
	int ret = Sum(100);
	cout << ret;
	return 0;
}

比如这个代码,把这个代码转化为非递归特别简单,一个for循环即可搞定。

	int ret = 0;
	for (int i = 1; i <= 100; i++)
	{
		ret += i;
	}
	cout << ret;

那么一个复杂的代码呢?比如快速排序,归并排序

快速排序非递归

快速排序在递归的时候,每次都是把区间压入栈中,
这是一个快排的代码,前后指针版本的。
在这里插入图片描述
从代码的最后,每次都是把基准值的左面先压入栈中,然后在继续划分,直到不能在划分为止,这个时候,就开始划分基准值的右面,直到完成排序。根据这个思想我们可以考虑手动模拟一个栈,然后用非递归来实现快排。


C++有库函数可以直接使用,在这里就不再写栈是怎么实现的了,不会的点击->传送门。
因为是把栈的左右区间压入,还要在定义一个pair<int,int>,不知道这个是什么的,也可以使用结构体,让结构体里面存左右区间也行

typedef pair<int, int> PII;

typedef struct Stack
{
	int l, r;
}stk;
  1. 首先,把最开始的区间压入栈中,然后以栈是否为空做为循环条件进行判断
  2. 定义临时变量存储左(begin)右(end)区间,然后用我们之前学过的快排,来模拟这一过程,快排里不在使用递归。
  3. 将区间划分,并压入栈中
  4. 重复2和3,直到循环结束,此时排序完成

代码如下

//挖坑法的快排代码
int PartSort2(int* a, int l, int r)
{
	//if (l >= r)
	//{
	//	return;
	//}

	int key = a[l];
	int hole = l;

	int bg = l;
	int ed = r;

	while (l < r)
	{
		while (l < r && a[r] >= key)
		{
			r--;
		}
		a[hole] = a[r];
		hole = r;

		while (l < r && a[l] <= key)
		{
			l++;
		}

		a[hole] = a[l];
		hole = l;

	}

	a[hole] = key;

	return hole;
}

//非递归形式实现快排
void QuickSort(int* a, int l, int r)
{

	stack<PII> stk;
	stk.push({ l, r });

	while (!stk.empty())
	{
		auto t = stk.top();
		stk.pop();
		
		int begin = t.first;
		int end = t.second;

		if (begin >= end)
		{
			continue;
		}
		
		int key_i = PartSort2(a, begin, end);

		stk.push({ begin,key_i - 1 });
		stk.push({ key_i + 1, end });

	}
}

在这里插入图片描述

归并排序的非递归

归并排序的非递归,这玩意有难度,学了好久才学会。
归并排序并不是用栈去实现的,因为归并排序是八一个序列分成n个子序列,然后归并归并归并,得到一个呈上升或者下降的序列,从这里,我们可以考虑直接从n个子序列开始归并,类似于树的后续遍历。
这里就要个问题了,归并排序需要一个临时数组,然后把临时数组赋值给原数组,从而得到有序序列,那么,这个拷贝的过程是一次性拷贝完,还是归并一次拷贝一次呢?
答案是这两种方式都可以,直接拷贝完的话呢,需要考虑的情况有点多,不推荐,归并一次拷贝一次最好。

  1. 首先定义一个gap,gap表示几几归并,刚开始肯定是一一归并,然后二二归并,四四归并
for (int i = 0; i < n; i += 2 * gap)
  1. 创建临时变量,将序列分隔开,
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;//建议自己手动模拟一下这个过程
  1. 对边界问题进行修正
if (end1 >= n || begin2 >= n)
{
	break;
}

if (end2 >= n)
{
	end2 = n - 1;
}
  1. 开始归并
while (begin1 <= end1 && begin2 <= end2)
{
	if (a[begin1] < a[begin2])
	{
		tmp[j++] = a[begin1++];
	}
	else
	{
		tmp[j++] = a[begin2++];
	}
}

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

while (begin2 <= end2)
{
	tmp[j++] = a[begin2++];
}
  1. 将临时数组拷贝到原数组中
memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
  1. 将gap扩大
		gap *= 2;

  1. 重复上述2,3,4,5,6操作
  2. 释放掉临时数组
	free(tmp);


代码如下

void UMergeSort(int* a, int n ,int* tmp)
{
	int gap = 1;

	while (gap < n)
	{
		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;

			if (end1 >= n || begin2 >= n)
			{
				break;
			}

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

			int j = i;

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

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

			while (begin2 <= end2)
			{
				tmp[j++] = a[begin2++];
			}

			memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
		}
		gap *= 2;
	}
	free(tmp);
}


void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * (n));
	UMergeSort(a, n,tmp);

}

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

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

相关文章

学习风`宇blog的websocket模块

文章目录后端代码引入依赖WebSocketConfigWebSocketServiceImpl分析tb_chat_record表WebSocketServiceImplChatConfigurator聊天消息ChatTypeEnumsWebsocketMessageDTO后端 代码 引入依赖 仅需引入以下依赖 <!-- websocket依赖 --> <dependency><groupId>…

ACM8629 立体声50W/100W单声道I2S数字输入D类音频功放IC

概述 ACM8629 一款高度集成、高效率的双通道数字输入功放。供电电压范围在4.5V-26.4V,数字接口电源支持3.3V 。在4 欧负载&#xff0c;BTL模式下输出功率可以到250W1%THDN&#xff0c;在2欧负载&#xff0c;PBTL模式下单通道可以输出1100W 1%THDN. ACM8629采用新型PWM脉宽调制架…

全国青少年软件编程(Scratch)等级考试二级考试真题2023年3月——持续更新.....

一、单选题(共25题,共50分) 1. 小猫的程序如图所示,积木块的颜色与球的颜色一致。点击绿旗执行程序后,下列说法正确的是?( ) A.小猫一直在左右移动,嘴里一直说着“抓到了”。 B.小猫会碰到球,然后停止。 C.小猫一直在左右移动,嘴里一直说着“别跑” D.小猫会碰到球,…

2023MatherCup杯三人小队手搓!(C 题 电商物流网络包裹应急调运与结构优化问题)

一个不知名大学生&#xff0c;江湖人称菜狗original author: Jacky LiEmail : 3435673055qq.com Time of completion&#xff1a;2023.4.16 Last edited: 2023.4.16 实际完成时间&#xff1a;2023/4/17 0:52 Mathematical modeling Author: HandSome Wang、BigTall Hu、Jacky L…

一个Email简单高效处理.Net开源库

推荐一个可处理电子邮件消息开源库&#xff0c;可用于消息解析、消息创建、消息修改和消息发送等功能。 项目简介 这是一个基于C#开发的&#xff0c;针对MIME&#xff08;多用途邮件扩展&#xff09;消息创建与解析&#xff0c;该项目简单易用、可用于消息解析、消息创建、消…

【Pytorch】神经网络搭建

在之前我们学习了如何用Pytorch去导入我们的数据和数据集&#xff0c;并且对数据进行预处理。接下来我们就需要学习如何利用Pytorch去构建我们的神经网络了。 目录 基本网络框架Module搭建 卷积层 从conv2d方法了解原理 从Conv2d方法了解使用 池化层 填充层 非线性层 …

Node实现 Socket 通信

socket 通信流程 Socket通信&#xff0c;首先要知道 Socket 是什么&#xff0c;就是网络上的两个程序通过一个双向的通信连接实现数据的交换&#xff0c;这个连接的一端被称为 socket &#xff0c;举一个简单的例子就是两个人在线上进行聊天&#xff0c;即线上通信&#xff0c…

充电桩检测设备TK4860E交流充电桩检定装置

产品特点 充电桩检测设备内置5.28 kW单相交流负载&#xff0c;无需携带额外负载进行测试。 宽动态范围测量技术&#xff0c;避免充电桩输出波动引起的测量风险。 ms级电能刷新速度&#xff0c;减少充电桩与标准仪器在非同步累积电能过程中引入的误差&#xff0c;提高累积电能…

【C++11那些事儿(二)】

文章目录一、新的类功能1.1 默认成员函数1.2 强制生成默认函数的关键字default1.3 禁止生成默认函数的关键字delete二、lambda表达式2.1 语法2.2 捕捉列表说明2.3 函数对象与lambda表达式一、新的类功能 1.1 默认成员函数 原来C类中&#xff0c;有6个默认成员函数&#xff1a…

性能测试总结-根据工作经验总结还比较全面

性能测试总结性能测试理论性能测试的策略基准测试负载测试稳定性测试压力测试并发测试性能测试的指标响应时间并发数吞吐量资源指标性能测试流程性能测试工具JMeter基本使用元件构成线程组jmeter的分布式使用jmeter测试报告常用插件性能测试的计算1.根据请求数明细数据计算满足…

【MySQL】多表查询

文章目录&#x1f389;多表查询&#x1f388;3.1 内连接查询&#x1f388;3.2 外连接查询&#x1f388;3.3 子查询最后说一句&#x1f389;多表查询 &#x1f388;3.1 内连接查询 语法 -- 隐式内连接 SELECT 字段列表 FROM 表1,表2… WHERE 条件;-- 显示内连接 SELECT 字段列…

LeetCode 特训 ---- Week1

目录 LeetCode 特训 --- Week1 两数之和 最长回文子串 删除有序数组中的重复项 删除有序数组中的重复项Ⅱ 删除链表中的重复元素 移动0 旋转链表 分隔链表 快慢指针&#xff08;前后指针&#xff09;用的好&#xff0c;链表&#xff0c;数组起码轻松打十个。 LeetCode…

videoPictureInPicture,视频画中画播放初探

从Chrome 70版本开始video元素开始支持画中画播放&#xff0c;简单写个demo体验一下 简介 在触发画中画之后视频会始终在右下角悬浮&#xff0c;不论是否在当前标签页或者是浏览器是否最小化。 可以在chrome内看任意视频时点击控制条的画中画按钮即可。 API 文档&#xff1…

人工智能机器人技术概述

移动机器人是一种能够在其环境中移动的自主或半自主机器人系统&#xff0c;通常是通过轮子或履带进行移动。这些机器人旨在在各种环境中执行各种任务&#xff0c;包括探索、监视、检查、运输和操作&#xff0c;包括室内和室外空间、危险区域甚至其他星球。 移动机器人配备传感…

日常记录:天梯赛练习集L1-048 矩阵A乘以B

题目&#xff1a; 给定两个矩阵A和B&#xff0c;要求你计算它们的乘积矩阵AB。需要注意的是&#xff0c;只有规模匹配的矩阵才可以相乘。即若A有Ra​行、Ca​列&#xff0c;B有Rb​行、Cb​列&#xff0c;则只有Ca​与Rb​相等时&#xff0c;两个矩阵才能相乘。 输入格式&…

【JAVA-模块四 流程控制语句】

JAVA-模块四 流程控制语句一 选择分支语句&#xff1a;if语句if第一种格式&#xff1a;if第二种格式 双分支&#xff1a;if的第三种格式&#xff1a;多条件分支switch多分支&#xff1a;注意&#xff1a;if语句和swich语句如何选择&#xff1a;二 循环语句&#xff1a;2.1 for循…

聚焦弹性问题,杭州铭师堂的 Serverless 之路

作者&#xff1a;王彬、朱磊、史明伟 得益于互联网的发展&#xff0c;知识的传播有了新的载体&#xff0c;使用在线学习平台的学生规模逐年增长&#xff0c;越来越多学生在线上获取和使用学习资源&#xff0c;其中教育科技企业是比较独特的存在&#xff0c;他们担当的不仅仅是…

Mars3D集成到ruoyi管理系统

尽管Mars3d的官网上提供了详尽的文档和API参考手册&#xff0c;但是在集成至ruoyi后天管理系统中时&#xff0c;还是碰到了不少问题&#xff1a; npm安装方式&#xff0c;若只安装mars3d&#xff0c;会提示找不到mars3d-cesium引用cesium相关库的时候&#xff0c;报404错误 这…

MongoDB基础学习总结及SpringBoot项目中的整合

前言 MongoDB 如今是最流行的 NoSQL 数据库之一&#xff0c;被广泛应用于各行各业中&#xff0c;很多创业公司数据库选型就直接使用了 MongoDB。MongoDB一经推出就受到了广大社区的热爱&#xff0c;可以说是对程序员最友好的一种数据库之一&#xff0c;下面主要是笔者在平常的…

大学刚毕业,用10000小时,走进字节跳动拿了offer

前言&#xff1a; 没有绝对的天才&#xff0c;只有持续不断的付出。对于我们每一个平凡人来说&#xff0c;改变命运只能依靠努力幸运&#xff0c;但如果你不够幸运&#xff0c;那就只能拉高努力的占比。 2020年7月&#xff0c;我有幸成为了字节跳动的一名测试开发&#xff0c…