• 初学者理解递归


    理解递归:

    三道题套路解决递归问题 | lyl's blog (lyl0724.github.io)

    写递归需要知道:

    首先是终止条件

    然后是在方法体哪里调用自己这个方法,即在哪里自己调用自己

    再就是需要return什么

    1.理解递归的一种方式

    以leetcode第21题举例, 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

    关于return L1的个人理解: 递归的核心在于,我只关注我这一层要干什么,返回什么,至于我的下一层(规模减1),我不管,我就是甩手掌柜.

    好,现在我要merge L1,L2.我要怎么做?

    • 显然,如果L1空或L2空,我直接返回L1或L2就行,这很好理解.

    • 如果L1第一个元素小于L2的? 那我得把L1的这个元素放到最前面,至于后面的那串长啥样 ,我不管. 我只要接过下级员工干完活后给我的包裹, 然后把我干的活附上去(令L1->next = 这个包裹)就行

    • 这个包裹是下级员工干的活,即merge(L1->next, L2)

    我该返回啥?

    • 现在不管我的下一层干了什么,又返回了什么给我, 我只要知道,假设我的工具人们都完成了任务, 那我的任务也就完成了,可以返回最终结果了

    • 最终结果就是我一开始接手的L1头结点+下级员工给我的大包裹,要一并交上去, 这样我的boss才能根据我给它的L1头节点往下找,检查我完成的工作

    1. class Solution {
    2. public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    3. if (l1 == null) {
    4. return l2;
    5. }
    6. else if (l2 == null) {
    7. return l1;
    8. }
    9. else if (l1.val < l2.val) {
    10. l1.next = mergeTwoLists(l1.next, l2);
    11. return l1;
    12. }
    13. else {
    14. l2.next = mergeTwoLists(l1, l2.next);
    15. return l2;
    16. }
    17. }
    18. }

    2.再次理解递归

    注:使用递归时,当我们递归到最后一层,就是即将回溯但还没回溯的最后一层,在这一层我们是只会进入终止条件,在终止条件这里终止,然后向回回溯,不会再运行下面的语句。

    也就是说,我们终止条件下面的语句,就不是写给最后一层递归的。。。

    当然,任何一层都有可能成为最后一层,主要是看它是否符合终止条件,符合了,向下的递归结束,开始回溯

    这里我们以leetcode101举例,判断一棵树是否轴对称

    递归的难点在于:找到可以递归的点 为什么很多人觉得递归一看就会,一写就废。 或者说是自己写无法写出来,关键就是你对递归理解的深不深。

    对于此题: 递归的点怎么找?从拿到题的第一时间开始,思路如下:

    1.怎么判断一棵树是不是对称二叉树? 答案:如果所给根节点,为空,那么是对称。如果不为空的话,当他的左子树与右子树对称时,他对称

    2.那么怎么知道左子树与右子树对不对称呢?在这我直接叫为左树和右树 答案:如果左树的左孩子与右树的右孩子对称,左树的右孩子与右树的左孩子对称,那么这个左树和右树就对称。

    仔细读这句话,是不是有点绕?怎么感觉有一个功能A我想实现,但我去实现A的时候又要用到A实现后的功能呢?

    当你思考到这里的时候,递归点已经出现了: 递归点:我在尝试判断左树与右树对称的条件时,发现其跟两树的孩子的对称情况有关系。

    想到这里,你不必有太多疑问,上手去按思路写代码,函数A(左树,右树)功能是返回是否对称

    def 函数A(左树,右树): 左树节点值等于右树节点值 且 函数A(左树的左子树,右树的右子树),函数A(左树的右子树,右树的左子树)均为真 才返回真

    实现完毕。。。

    1. public boolean isSymmetric(TreeNode root) {
    2. if (root==null){
    3. return true;
    4. }
    5. return digui(root.left, root.right);
    6. }
    7. public boolean digui(TreeNode left,TreeNode right){
    8. //终止条件,下面两个都是
    9. if (left==null&&right==null){
    10. return true;
    11. }
    12. if (left==null||right==null||right.val!=left.val){
    13. return false;
    14. }
    15. return digui(left.left, right.right)&&digui(left.right, right.left);
    16. }

    3.再再次理解递归

    leetcode206,翻转链表,给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

    这次是一个关于链表的题,并不是很复杂的递归,甚至递归的方法不在方法体中,它直接将方法体当做返回值进行return,总之就是递归方法先传进来两个相连的节点,将这两个节点反转之后,在调用这个递归函数,将第二个节点以及它的下一个节点传入,继续进行翻转,直到翻转到最后一个节点。

    我们在这道题里面可以发现,递归中我们的终止条件并不仅仅是终止条件这么简单,它return回去的值,有时候会在方法体中被用到,有时候会直接当做最终结果返回。

    1. public ListNode reverseList1(ListNode head) {
    2. return reverse(null, head);
    3. }
    4. //使用递归
    5. public ListNode reverse(ListNode pre,ListNode cur) {
    6. if (cur==null){
    7. return pre;
    8. }
    9. ListNode next=cur.next;
    10. cur.next=pre;
    11. return reverse(cur,next);
    12. }

    注:上面对题目的分析部分某些是个人的理解,某些则取自leetcode评论区,个人觉得大佬们分析的很好!有助于大家对递归的理解~

  • 相关阅读:
    django对比数据并调用企业微信接口群发
    安全防御—密码学
    Java 24 Design Pattern 之
    2023最新的接口自动化测试完整版
    如何将安防视频监控系统/视频云存储EasyCVR平台推流到公网直播间?
    springboot整合Redis报错:java.io.IOException 远程主机强迫关闭了一个现有的连接
    校园棒球运动会运营策划案·棒球联盟
    码云/GitHub Fork代码仓并提交PR代码
    npm如何发布自己的插件包
    老卫带你学---leetcode刷题(17. 电话号码的字母组合)
  • 原文地址:https://blog.csdn.net/weixin_52394141/article/details/127868957