首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

【递归,搜索与回溯算法 & 递归算法】递归算法入门详解:递归算法小专题

  • 25-03-04 20:42
  • 4621
  • 7286
blog.csdn.net

  

 


汉诺塔问题


     题目解析    



     什么是汉诺塔问题     


  1. 通过三根柱子,把最左边柱子上的圆环移动到最右边;
  2. 每次只能移动一个圆环;
  3. 小圆环必须在大圆环之上 ;


      汉诺塔问题的简单过程模拟     



    算法原理    


     为什么汉诺塔问题可以使用递归来解决呢?(Why?)    

    1. 如何解决汉诺塔问题?    


要想知道为什么能使用递归来解决汉诺塔问题,我们先来解决:如何解决汉诺塔问题?


     总结规律     


     解决两层汉诺塔的基本流程    


对于2层汉诺塔的解决步骤:


    解决三层汉诺塔的基本流程    


对于3层汉诺塔的解决步骤:

所以,当胖子有 n 个时,先把底下最大的盘子移动到 poleC 上,那么就需要把上面 n-1 个盘子移动到 poleB 上;那当盘子有 n-1 个,也是同样的操作;

    规律    

当 N=n 时,我们只需要把第 n 个盘子先移动到 poleC 即可,此时就需要把 n -1 个盘子先移动到辅助杆 poleB 上,那么这 n-1 个盘子怎么移动呢?

在 N=n-1 的时候,我们已经解决过这个问题了,直接拿来用即可;


     解法:递归 (How?)    



     重复子问题 (函数头)   


对于上图列出的情况,无论 N 等于多少,都要重复上面的 1,2,3 步: 


重复子问题就是把某一根柱子x上的盘子,借助某一根柱子y,放到另一个柱子z上:

  •      设置函数头:    

void dfs( poleX , poleY, poleZ, 盘子数量) ;


  •      作用:   

给 dfs()传入三个柱子和盘子的数量,dfs()就可以把 x 上的 N 个盘子,借助 y 的帮助,传入到 z 上;


    设置函数体    


设计函数体,只需要关心某一个子问题在做什么,也就是 N 为某一个值时,盘子的移动流程;


  • 第一步:借助 z 柱子,把 x 上的 n-1 个盘子移动到 y 上

  • 函数体的第一步: dfs( x , z , y , n-1)

  • 第二步:把 x 底下最大的盘子,从 x 移动到 z :

  • 函数体的第二步:x.back(),这个方法的作用是,把 x 最底下的盘子移动到 z 上;

  • 第三步:把 y 上的 n-1 个盘子,借助 x 移动到 z 上: 

  • 函数体的第三步 dfs( y, x, z, n-1);

    递归出口     


我们发现,当 N=1 时的处理过程,和 N>1 的处理过程完全不一样: 

 

当 N=1,就不需要借助辅助柱子,直接移动即可;

所以N=1时处理盘子的方法,就是汉诺塔问题的递归出口; x.back();


    编写代码    


    方法一:递归     



    递归细节展开图    


    方法二:不讲武德    

如果是笔试题,可以直接这样 ac,因为:

但是如果是面试题这样写,就回家等通知吧哈哈哈!!!


合并两个有序链表


     题目解析    



    算法原理    


     解法:递归    


    (1)寻找重复子问题 (设计函数头)    


我们定义两个指针 left1,left2, 返回两个链表合并后的有序链表的头指针

因为这两个指针都是升序的,在刚开始遍历链表时,比较两条链表头节点,以较小节点作为最终合并链表后,返回的头节点;

接下来,就比较剩下的两条链表,找到两个新头节点中较小的节点,作为接下来新链表的下一个节点;

所以重复的子问题:是合并链表,返回新链表的下一个节点;

所以函数头为 Node dfs(Node l1, Node l2),就是合并完链表,返回每次合并链表的末尾节点;


  (2)某一个子问题的具体流程 (函数体)     


  • 第一步,比较大小;假设较小值为 l1;

  • 第二步,让较小节点连接两条链表剩余部分合并后的结果即可;l1.next = dfs(l1.next ,  l2);

  • 返回合并链表的新加入节点 return l1;(假设 l1 ,l2 两个指针,l1指向较小的节点)

 (3)递归出口        


什么时候子问题不能再细分了呢?当两个指针其中一个指向对应链表的尾节点,就可以停止递归,返回另一个指针即可;此时另一条链表的剩余部分,会和合并链表的尾节点连接;


 (4)编写代码    


注意:无论当前节点是子问题还是递归出口,在拼接好剩余链表的合并部分后,都要返回当前节点;


    拓展    


     (1) 递归问题和循环问题可以互相转换     


递归和循环都是方法处理一个子问题,两者可以通过修改互相转换


    (2) 使用递归&循环的时机    


而如果通过递归解决的问题,要通过循环来解决,我们就需要借助栈,因为在递归时的一个代码块方法,在递归后该方法节点并没有完全执行完,等下层节点回溯之后,还会继续执行方法剩余部分;


但是如果使用循环,我们就需要使用栈来保存方法节点未执行完的信息,如果是解决汉诺塔问题,递归代码会非常简洁,而使用循环,那么代码成本就会大幅度提高;

但是如果递归展开图不是树形结构,那么把递归修改成循环也是比较容易的(不绝对),如遍历数组;对于使用递归遍历数组,子问题就是,给一个数组元素,把剩余的数组元素打印出来;

所以对于大多数情况下,如果递归图越复杂,越适合使用递归,反之则适合使用循环;


    (3) 修改递归顺序可以改变遍历顺序     



我们修改这两条代码的先后顺序,虽然递归图相同,但是遍历数组的顺序会被修改;


    (4) 通过打印日志调试     



反转链表


     题目解析    



    算法原理    


     视角一: 从宏观角度看待问题     



 我们可以先让 head.next.next = head:


再让 head.next = null 即可翻转前两个节点:

但是此时会出现一个问题,就是第三个节点丢失了;如果先修改,会出现丢失节点信息的情况;

既然先修改前面的节点指向会出现节点信息丢失,我们就调整修改顺序: 

    算法思路:    

  1. 递归函数的含义: 交给你一个链表的头指针,你帮我逆序之后,返回逆序后的头结点;(想到递归函数的含义是这道题中最难的)
  2. 函数体: 先把当前结点之后的链表逆序,逆序完之后,把当前结点添加到逆序后的链表后面即可;
  3. 递归出口: 当前结点为空或者当前只有一个结点的时候,不用逆序,直接返回。

  • 1. 让当前节点后面的链表先逆置,并且把头节点返回(怎么返回,怎么逆置都不关心)
  • 2. 让当前节点添加到逆置的链表之后即可; 
  • 3. 如果当前节点为 null,结束递归

    视角二:把链表看成一棵树     


链表其实就是一棵树形结构,如果要把链表逆置,只需要对这棵树进行一次后序遍历,并且返回头节点即可;


     动态模拟过程     


    编写代码    


给这个 dfs 传入节点指针,dfs自身就能把这个节点所连的链表逆置,并且把逆置链表的头节点返回:


两两交换链表中的节点


     题目解析    



    算法原理    


     解法:递归    




为什么 dfs 不是从节点2 往后开始逆置呢?我们的大问题是:把链表前两个节点和剩余链表全部逆置,前面两个节点是一个组合,所以 dfs 从第三个节点开始;怎么逆置我们不关心:


接下来,我们只需要把前两个节点进行交换;把交换后的结构,和交换好的链表连起来:

递归出口:如果是空节点,或者只剩下一个节点,那么就不需要交换,直接返回即可; 


    处理细节问题    


 需要记录一下原链表的第二个节点作为返回值,因为新链表的头节点位原链表的第二个节点;


    编写代码    




 Pow(x,n) - 快速幂


     题目解析    



    算法原理    


      解法一:暴力循环     


如果直接使用暴力循环,会超时: 


  如果当 n 非常大时,比如 x^(2^31-1),计算这个数会导致超时问题;


     解法二:使用递归实现快速幂      


思考:为什么暴力循环慢呢?

通过上图,我们可以类比出,当 n 非常大时,计算次数也会异常庞大,从而导致计算超时; 


 但是,我们可以使用快速幂的方式,来减小了计算的次数:

上面的计算次数,和时间复杂度相等;


    递归过程    



    编写代码    


    实现快速幂:递归      


 

注:本文转载自blog.csdn.net的OuO-1的文章"https://blog.csdn.net/2402_84916296/article/details/144481329"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2491) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

113
数据结构与算法
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top