深入浅出递归算法

文章目录

  • 递归思想
  • 递归的题目
    • 1.汉诺塔问题
      • 问题分析
      • 代码展示
    • 2.合并两个有序链表
      • 问题分析
      • 代码展示
    • 3.反转链表
      • 问题分析
      • 代码展示
    • 4.两两交换 链表中的节点
      • 问题分析
      • 代码展示
  • 总结

在这里插入图片描述

递归思想

递归就是将一个很大的问题拆分成子问题,然后再将子问题继续拆分,拆分成更小的子问题,最后直到不能拆分为止。
递归一共分为三个步骤,首先,我们要将一个问题拆为一些子问题,然后去看这些子问题是否有相同的方法可以继续拆分,所以递归的关键就是一个大问题是否能转换成相同类型的子问题,然后子问题是否又能继续转换成相同类型的子问题,注意这里我们就需要搞定我们这个递归的函数传递的参数具体需要什么,也就是(函数头),当我们确立了子问题之后,我们就需要进行函数体的书写了,书写函数体主要围绕单个子问题进行,因为,我们的一个大的问题都可以拆分为一个个的小的子问题,所以这些子问题都可以通过一个方法来处理,所以只需要对一个子问题进行书写函数体就行了,最后,我们需要防止无限递归,也就是递归的终止条件,向上归的过程。

总结:递归的方法

  1. 找到类型相同的子问题
  2. 对某个子问题进行函数体方法的书写
  3. 递归的出口----终止条件的判断

递归的题目

1.汉诺塔问题

问题分析

这里是引用
输入和输入:
在这里插入图片描述

在这里插入图片描述

首先我们来分析:
1.找到子问题

可以看到子问题很容易就出来了,我们不管有多少个盘子,我们都可以将上面的n-1个盘子看成一个整体,然后将上面n-1个盘子借助C移到B柱上,然后将最下面的盘子移到C上,然后再对上面n-1个盘子实行相同的方法,对上面n-1个盘子上的n-2个盘子用刚刚一样的方法。

这里子问题找到了,我们就可以确定我们的函数头和传递的参数了,对于上面的图我们传递的函数头就可以用下面类似方式写出:dfs(A,B,C,n).
2.用单个子问题寻找函数体
单个子问题是:
在这里插入图片描述
首先第一步是:dfs(A,C,B,n-1)

这句代码的意思是将A柱上n-1个盘子从A移到B上,借助C

第二部是:A.back()->C(伪代码)

将A上最后一个盘子移到C,当我进行了第一步递归之后,只剩下最后一个盘子了,所以,我需要将最后一个盘子移到C上

第三部:dfs(B,A,C,n-1)

将B柱上剩下的盘子移到C上,注意中间的过程我们不需要管,他会不断拆分,我们只需要找到同一的方法即可

3.递归出口
对于递归的出口,我们可以看上面的子问题,当我们只有一个盘子的时候我们就直降将这个盘子移到C柱上了,所以这里的最后的递归出口就是当只有一个盘子的时候。

代码展示

class Solution {
public:void dfs(vector<int>& x,vector<int>&y,vector<int>&z,int n){if(n==1)//当只有一个盘子的时候移到z柱上{//移到z柱上z.push_back(x.back());//将x柱上的值删除x.pop_back();return;}//将x柱上的整体的n-1个盘子整体移到y柱上dfs(x,z,y,n-1);//然后将x柱上剩下的一个盘子移动到z柱上z.push_back(x.back());x.pop_back();//最后将y柱上的剩下的盘子移动到z柱上即可,借助x柱,注意:y柱上有n-1个盘子dfs(y,x,z,n-1);}void hanota(vector<int>& a, vector<int>& b, vector<int>& c){dfs(a,b,c,a.size());}
};

2.合并两个有序链表

在这里插入图片描述
样例输入和输出:
在这里插入图片描述

问题分析

1.寻找子问题
这里其实我们可以选一个小的作为头,选好这个头之后将这个头去指向这个函数头,这个函数头就是去给我们合并的函数。
确定函数头:dfs(l1,l2)
2.根据单个子问题找到函数体

这里我们可以通过子问题找到函数体:首先函数头是传递两个链表的头,然后返回的是新的头结点,所以这里我们只需要取两个链表的中的小的为新的头,然后去链接剩下的两个链表

函数体:min->next=dfs(min->next,other)

这里大致就可以将函数体写成这样,小的链表的头指向小的链表的剩下的部分和另一个链表

3.递归出口
递归出口:当有一个函数为空时直接返回另一个链表,注意:这里其实可以这样想,当我们链接当一个链表为空的时候是不是另一个链表链接上去肯定是有序的,所以这里我们只需要返回另一个部位空的链表即可。

代码展示

class Solution 
{
public:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {if(list1==nullptr)return list2;if(list2==nullptr)return list1;if(list1->val<=list2->val){list1->next=mergeTwoLists(list1->next,list2);//list1作为头结点合并list1->next,和list2return list1;}else{list2->next=mergeTwoLists(list1,list2->next);//连接list2->next和list1return list2;}}
};

3.反转链表

在这里插入图片描述

问题分析

这道题我们其实也可以用递归来做,我们要将整个链表翻转其实可以看做将1后面的链表翻转,剩下的链表翻转又可以分解成剩下的链表的剩下的部分翻转,接下来我用一个图方便理解

在这里插入图片描述
大概就是上图的意思,我们先深度优先遍历到最后一个节点,然后再向上翻转注意,这里我没有志向nullptr,但是我们每次翻转的时候都要指向nullptr,这是为了递归的统一。

函数头
函数头:dfs(head)

函数体

函数体:newhead=reverseList(head->next);
我们只需要创一个新的头来等于剩下的翻转过的链表,注意:这里我们翻转过的链表是抽象的递归,具体是怎么完成的计算机会完成,我们只需要给出方法,这里我们已经得到了翻转链表的方法之后,我们就可以直接将就的head与翻转过的链表进行连接即可。

递归出口
当当前节点指向的下一个是nullptr的时候或者当当前节点是nullptr的时候就直接返回当前节点。

代码展示

class Solution 
{
public:ListNode* reverseList(ListNode* head) {//出口if(head==nullptr||head->next==nullptr){return head;}ListNode*newhead=reverseList(head->next);//原本head->next是head的next但是现在要反转过来//就要把head的next节点指向自己就是head->next->next=head;head->next->next=head;head->next=nullptr;return newhead;}
};

4.两两交换 链表中的节点

这里是引用

问题分析

这里这道题和上一道题其实很相似,我们其实只需要将后面所有应该交换的节点全部交换了,然后将后面节点的新的头给前面两个节点连接上即可,注意这里后面是怎么交换的我们也不知道,但是我们用一个函数去让他交换,我们让他交换前两个节点后面剩下的节点,它会转换成叫唤后面剩下的节点除了前两个节点外的剩下的节点,这样一直递归下去,直到遇到递归出口为止。

函数头
函数头:dfs(head)

函数体
注意这里我们返回的交换之后链表的新的头,意思就是当我们把除了1和2节点外的所有节点外的节点交换之后,会返回一个新的节点,注意看上面给出的示例,意思就是当我们用一个递归表示的话,返回的就是4这个节点,所以我们可以直接用tmp存储这个节点,然后将前面两个节点的指向进行变化即可。
函数体:

ListNode*tmp=swapPairs(head->next->next);
auto next=head->next;
head->next->next=head;
head->next=tmp;

递归出口
这里的递归出口还是当遇到空节点或者下一个节点是空节点直接返回当前节点

代码展示

class Solution {
public:ListNode* swapPairs(ListNode* head) {if(head==nullptr||head->next==nullptr){return head;}ListNode*tmp=swapPairs(head->next->next);auto next=head->next;head->next->next=head;head->next=tmp;return next;}
};

总结

递归算法作为计算机科学中的一种基本思想,展现了其简洁优雅和强大的解决问题能力。从数学计算到复杂的数据结构处理,递归提供了一种自然且直观的方法来分解和解决问题。尽管递归在某些情况下可能带来性能和资源上的挑战,但通过优化技术如记忆化存储和尾递归优化,我们可以克服这些困难,实现高效的递归算法。

递归不仅仅是编程技术,更是一种思维方式。通过理解递归的本质,我们能够培养出更好的抽象思维能力,解决更复杂的计算问题。希望这篇博客能够帮助你更好地理解递归算法,并激发你在编程中更多地应用和探索这一强大的工具。

无论你是编程新手还是经验丰富的开发者,掌握递归算法都会为你提供一种新的视角,帮助你在算法和数据结构的学习和应用中取得更大的进步。让我们一起拥抱递归的美妙世界,不断挑战和提升自我!

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

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

相关文章

【linux-IMX6ULL-字符设备驱动简单框架实验】

目录 1. 字符设备驱动简介1.1 重要函数1.2 简单框架代码流程1.3 linux中关于驱动的重要命令 2. 字符设备驱动简单框架编写2.1 添加LICENSE信息2.2 驱动模块的入口与出口2.3 入口和出口函数的编写2.4 设备操作结构体定义2.4.1 结构体函数内容填充 3. 应用程序简介&#xff1a;4.…

Design to code(2)

【碎碎念】从七点到十一点&#xff0c;累计用时4个小时完成的代码翻译Σ(&#xffe3;。&#xffe3;ノ)ノ DCDS图 顺序图&#xff08;支付过程&#xff09; 交互图&#xff08;订单&#xff09; 我的代码 Payment public class Payment { //定义支付订单金额 private…

FL Studio2025中文最新版本专业编曲软件有哪些新功能?

FL Studio 21&#xff0c;也被音乐制作爱好者亲切地称为“水果编曲软件”&#xff0c;是比利时的Image-Line公司研发的一款完整的音乐制作环境或数字音频工作站&#xff08;DAW&#xff09;。自从1990年代推出以来&#xff0c;FL Studio 以其直观的用户界面、丰富的插件支持和强…

玩机社区 - 2024年最美社区源码开源

玩机社区 - 2024年最美社区源码开源 教程源码文档都内置到压缩包了 https://pan.baidu.com/s/1xwcscTne-JMbmKEntiuAuA?pwd78oi

逻辑分析仪 - 采样率/采样深度

采样深度&#xff08;Sampling Depth&#xff09; 采样深度指的是逻辑分析仪在一次捕获过程中可以记录的最大样本数量。简单来说&#xff0c;采样深度越大&#xff0c;逻辑分析仪可以记录的数据量就越多。这对于分析长时间的信号变化或复杂的信号序列非常重要。 采样率&#…

2024年5月23日 (周四) 叶子游戏新闻

《Unclogged》Steam页面上线 马桶主题恐怖逃脱解谜Brody制作并发行&#xff0c;一款奇葩创意马桶主题恐怖逃脱解谜新游《Unclogged》Steam页面上线&#xff0c;本作暂不支持中文。 Meta人工智能主管杨立昆 大语言模型不会达到人类智能水平IT之家今日&#xff08;5月23日&#x…

数据防泄漏系统哪个好用,给文件加密的软件

数据防泄露&#xff08;Data Leakage Prevention&#xff0c;DLP&#xff09;是指通过一定的技术手段&#xff0c;防止组织指定&#xff08;重要或敏感的&#xff09;数据或信息资产以违反安全策略规定的形式流出组织的一种策略。 信息防泄露以文档加密技术为核心&#xff0c;…

顺序表及其应用

掌握顺序表的初始化&#xff0c;初始化、查找、插入、删除、遍历、查看实际长度等操作 内容 从键盘输入n个整数&#xff0c;创建顺序表。【创建长度为n的顺序表】从键盘输入1个整数x&#xff0c;在顺序表中查找x所在的位置。若找到&#xff0c;输出该元素所在的位置(即数组下标…

SQL开窗函数

文章目录 概念&#xff1a;语法&#xff1a;常用的窗口函数及示例&#xff1a;求平均值&#xff1a;AVG() &#xff1a;求和&#xff1a;SUM():求排名&#xff1a;移动平均计数COUNT():求最大MXA()/小MIN()值求分区内的最大/最小值求当前行的前/后一个值 概念&#xff1a; 开窗…

同旺科技 FLUKE ADPT 隔离版发布 ---- 说明书

所需设备&#xff1a; 1、FLUKE ADPT 隔离版 内附链接&#xff1b; 应用于&#xff1a;福禄克Fluke 12E / 15BMax / 17B Max / 101 / 106 / 107 应用于&#xff1a;福禄克Fluke 15B / 17B / 18B

利用文本图像对比模型进行虚假信息检测

Harnessing the Power of Text-image Contrastive Models for Automatic Detection of Online Misinformation 论文地址: CVPR 2023 Open Access Repositoryhttps://openaccess.thecvf.com/content/CVPR2023W/WMF/html/Chen_Harnessing_the_Power_of_Text-Image_Contrastive_…

力扣周赛398题解

特殊数组Ⅰ 如果数组的每一对相邻元素都是两个奇偶性不同的数字&#xff0c;则该数组被认为是一个 特殊数组 。 Aging 有一个整数数组 nums。如果 nums 是一个 特殊数组 &#xff0c;返回 true&#xff0c;否则返回 false。 示例 1&#xff1a; 输入&#xff1a;nums [1] …

【C++】<知识点> 标准和文件的输入输出

目录 一、输入输出操作 1. 相关的类 2. 标准流对象 3. istream类的成员函数 二、流操纵算子 1. 整数流的基数 2. 浮点数精度的流操纵算子 3. 域宽的流操纵算子 4. 其他的流操纵算子 5. 用户自定义流操纵算子 三、文件读写 1. 文本文件的读写 2. 二进制文件的读写 3. 文件读写…

vue 点击复制文本到剪贴板

一、首先在vue文件的template中定义复制按钮 <div size"small" v-if"item.prop jadeCode" class"cell-container"><span>{{ scope.row.jadeCode }}</span> <button click"handleCopy(scope.row.jadeCode)" clas…

K8s是如何Watch的?

1. 概述 进入 K8s 的世界&#xff0c;会发现几乎所有对象都被抽象为了资源(Resource)&#xff0c;包括 K8s Core Resources(Pod, Service, Namespace 等)、CRD、APIService 扩展的资源类型。同时 K8s 底层将这些资源统一抽象为了 RESTful 的存储(Storage)&#xff0c;一方面服…

jellyfish安装及使用(Bioinformatics工具-020)

01 背景 基因组survey以测序技术为基础&#xff0c;基于小片段文库的低深度测序&#xff0c;通过K-mer分析&#xff0c;快速获得基因组大小、杂合度、重复序列比例等基本信息&#xff0c;为制定该物种的全基因组de novo测序策略提供有效依据。 jellyfish (水母) 是一个用于快…

Docker-镜像迁移的三种方式=>备份恢复公有仓库私有仓库

制作好的镜像要被别人使用&#xff0c;有三种方式&#xff1a; 1.先备份镜像&#xff0c;别人通过u盘或者其它方式拷贝后&#xff0c;再恢复镜像&#xff0c;这种方式比较麻烦 2.将制作的镜像上传到公共镜像仓库&#xff0c;被别人拉取后使用&#xff0c;但可能存在网络不通畅或…

【零基础C语言】内存函数

前言&#xff1a; 我们之前学过strcpy&#xff0c;strcmp等等函数&#xff0c;他们可以拷贝字符串和比较字符串等等&#xff0c;那么有没有什么函数不光可以拷贝字符串还可以拷贝其他的数据呢&#xff0c;答案就是内存函数。 相较于字符串函数&#xff0c;内存函数可以拷贝的…

赎金信[简单]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给你两个字符串&#xff1a;ransomNote和magazine&#xff0c;判断ransomNote能不能由magazine里面的字符构成。如果可以&#xff0c;返回true&#xff1b;否则返回false。magazine中的每个字符只能在ransomNote中使用一次。 示例 …

DPDK实践之(1)dpdk基础使用

DPDK实践之(1)dpdk基础使用 Author: Once Day Date: 2024年5月19日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文档可参考专栏&#xff1a;Linux基础知识_Once…