当前位置:   article > 正文

递归与回溯(Java实现)_java 递归返回的还是上次的对象

java 递归返回的还是上次的对象

递归与回溯

一句话讲递归与回溯

递归:自己调用自己。本质就是找到前后的联系,找到递归的公式。

回溯:执行一次深度优先遍历(DFS),一条路走到底,走不通的时候,返回回来,继续执行,一直这样下去,直到回到起点。

递归

一般情况为:

    if (终止条件) {        
        return;    
    }    
    recursion(参数1);
  • 1
  • 2
  • 3
  • 4
例子:阶乘
  1. 阶乘

    public int recursion(int n) {
        //终止条件
        if (n == 1)
            return 1;
        //调用自己
        return n * recursion(n - 1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    递归过程:

在这里插入图片描述

求f(5)的时候,只需要求出f(4)即可,如果求f(4)我们要求出f(3)……,一层一层的调用,当n=1的时候,我们直接返回1,然后再一层一层的返回,直到返回f(5)为止。

一些较实际的情况:

    if (终止条件) {       
        return;    
    }
    可能有一些逻辑运算   
    recursion(参数1);    
    可能有一些逻辑运算    
    recursion(参数2);            
    ……    
    recursion(参数n);
    可能有一些逻辑运算
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
例子:反转链表
  1. 反转链表

    public static void reverseList(ListNode root) {
           //(终止条件)
         if (root == null)
             return ;
           //(递归调用)先打印下一个
         reverseList(root.next);
           //(逻辑处理)把后面的都打印完了在打印当前节点
         System.out.print(root.val + " ");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    结果如下:
    在这里插入图片描述

回溯

回溯的本质,其实是在递归基础上进行了改进

一般情况为:

if(不满足继续递归查找的条件,通常为界限判断)
    return;
if(满足查找条件)
    return 这个值/节点;
递归左边
递归右边
递归结果判断-回溯
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
例子:矩阵中的路径(剑指Offer12)

判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。

示例:

输入:board = [
			   ["a","b"],
               ["c","d"]
              ],
     word = "abcd"

输出:false
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 思路:回溯算法(DFS+剪枝)。遍历矩阵中所以字符,先朝一个方向搜索到底,再回溯至上个节点,再沿另一方向搜索,以此类推。在搜索中,遇到匹配不成功(如索引越界、此元素已访问、此矩阵元素和目标字符不同)的情况就立即返回。
  • 代码实现
class Solution {
    public boolean exist(char[][] board, String word) {
        char[] words = word.toCharArray();
        for(int i = 0; i < board.length; i++) {
            for(int j = 0; j < board[0].length; j++) {
                if(dfs(board, words, i, j, 0)) return true;
            }
        }
        return false;
    }
    //回溯
    boolean dfs(char[][] board, char[] word, int i, int j, int k) {
        //边界条件(越界、与目标元素不同)
        if(i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]) return false;
        //全部匹配完成
        if(k == word.length - 1) return true;
        
        char temp = board[i][j];
        board[i][j] = '\0';//将当前元素标记为'\0',不可再被访问
        //递归上下左右
        boolean res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) ||dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);

        board[i][j] = temp;//恢复其本身值
        //递归结果判断-回溯
        return res;//上面4个方向,只要有一个能查找到,就返回true
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
例子:二叉搜索树的最近公共祖先(剑指Offer68-Ⅱ)

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CUXcdhJx-1605416128009)(</images/OFfer68.PNG>)]

示例 1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
  • 1
  • 2
  • 3

示例 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
  • 1
  • 2
  • 3
  • 思想:通过递归对二叉树进行后序遍历,当遇到节点 p 或 q 时返回。从底至顶回溯,当节点 p, q 在节点 root 的异侧时,节点root 即为最近公共祖先,则向上返回 root 。

    p,q和root有三种情况:
    1). p,q在root左右,返回root
    2). p,q都在root左,返回lowestCommonAncestor( root.left, TreeNode p, TreeNode q)
    3). p,q都在root右,返回lowestCommonAncestor( root.right, TreeNode p, TreeNode q)

    • 递归终止条件
      if(root == null) return null;//越过叶子节点,返回null
      if(root == p) return p;//找到p,返回p
      if(root == q) return q;//找到q,返回q

    • 递归体
      递归左子节点,返回值为left
      TreeNode left = lowestCommonAncestor( root.left, TreeNode p, TreeNode q);
      递归右子节点,返回值为right
      TreeNode right = lowestCommonAncestor( root.right, TreeNode p, TreeNode q);

    • 递归结果
      1).left == null && right == null
      两边都没找到,返回null
      2).left == null
      右边找到,返回right
      3). right == null
      右边找到,返回left
      4).left != null && right != null
      说明p,q在root两侧,返回root

  • 图文过程详解
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 代码实现

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //终止条件
        if(root == null || root == p || root == q) return root;

        //递归体
        TreeNode left = lowestCommonAncestor( root.left, p, q);
        TreeNode right = lowestCommonAncestor( root.right, p, q);

        //递归结果判断-回溯
        if(left == null && right == null) return null;
        if(left == null) return right;
        if(right == null) return left;
        return root;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

结束语

算法小白对递归和回溯的理解只到这里,希望随着刷的题越来越多,理解也越来越清晰透彻,到时再来分享!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/590448
推荐阅读
相关标签
  

闽ICP备14008679号