递归与回溯(Java实现)

递归与回溯

一句话讲递归与回溯

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

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

递归

一般情况为:

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

    public int recursion(int n) {
        //终止条件
        if (n == 1)
            return 1;
        //调用自己
        return n * recursion(n - 1);
    }
    

    递归过程:

在这里插入图片描述

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

一些较实际的情况:

    if (终止条件) {       
        return;    
    }
    可能有一些逻辑运算   
    recursion(参数1);    
    可能有一些逻辑运算    
    recursion(参数2);            
    ……    
    recursion(参数n);
    可能有一些逻辑运算
例子:反转链表
  1. 反转链表

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

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

回溯

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

一般情况为:

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

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

示例:

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

输出:false
  • 思路:回溯算法(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
    }
}
例子:二叉搜索树的最近公共祖先(剑指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。

示例 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
  • 思想:通过递归对二叉树进行后序遍历,当遇到节点 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;
    }
}

结束语

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

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是迷宫问题的递归回溯算法的Java实现: ```java import java.util.*; public class MazeSolver { // 迷宫的地图 private int[][] maze; // 迷宫的行数和列数 private int rows, cols; // 路径栈,用来保存从起点到终点的路径 private Stack<Point> path; public MazeSolver(int[][] maze) { this.maze = maze; this.rows = maze.length; this.cols = maze[0].length; this.path = new Stack<>(); } public void solve() { // 从起点开始搜索 Point start = new Point(0, 0); if (search(start)) { System.out.println("找到了一条从起点到终点的路径:"); for (Point p : path) { System.out.println("(" + p.x + ", " + p.y + ")"); } } else { System.out.println("没有找到从起点到终点的路径!"); } } // 搜索从当前位置开始的路径 private boolean search(Point cur) { // 如果当前位置是终点,则搜索成功 if (cur.x == rows - 1 && cur.y == cols - 1) { path.push(cur); return true; } // 如果当前位置不是终点,则尝试向四个方向搜索 if (maze[cur.x][cur.y] == 0) { // 如果当前位置是通路 maze[cur.x][cur.y] = -1; // 标记为已访问 path.push(cur); // 将当前位置加入路径 // 向右搜索 if (cur.y < cols - 1 && search(new Point(cur.x, cur.y + 1))) { return true; } // 向下搜索 if (cur.x < rows - 1 && search(new Point(cur.x + 1, cur.y))) { return true; } // 向左搜索 if (cur.y > 0 && search(new Point(cur.x, cur.y - 1))) { return true; } // 向上搜索 if (cur.x > 0 && search(new Point(cur.x - 1, cur.y))) { return true; } path.pop(); // 如果四个方向都搜索失败,则将当前位置从路径中删除 maze[cur.x][cur.y] = 0; // 标记为未访问 } return false; } public static void main(String[] args) { int[][] maze = { {0, 1, 0, 0, 0}, {0, 1, 0, 1, 0}, {0, 0, 0, 0, 0}, {0, 1, 1, 1, 0}, {0, 0, 0, 1, 0} }; MazeSolver solver = new MazeSolver(maze); solver.solve(); } } class Point { public int x; public int y; public Point(int x, int y) { this.x = x; this.y = y; } } ``` 这个程序使用了递归回溯算法来搜索迷宫中从起点到终点的路径。程序首先从起点开始搜索,如果当前位置是终点,则搜索成功;否则,程序尝试向四个方向(右、下、左、上)搜索。如果搜索成功,则返回 true;否则,程序将当前位置从路径中删除,并且将当前位置标记为未访问,然后返回 false。程序最后输出从起点到终点的路径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值