搜索与回溯算法(Java)

1. 剑指 Offer 12. 矩阵中的路径

原题链接
在这里插入图片描述在这里插入图片描述
深度优先搜索

/*
	1. dfs,先朝一个方向搜到底,再回溯至上个节点,沿另一个方向搜索,以此类推。
	2. dfs怎么写?
	   1) 参数(board, word, i, j, k)i,j是board中的索引 k是word中的索引
	   2) 返回值(结束条件):
	    false: 索引越界/字符不匹配
	   	true: k==word.length-1, 说明完成了全部匹配
	   3) 满足条件,递归向四个方向搜索
	3. 递归
	   1) 标记已访问元素防止递归时重复搜索。board[i][j] = '\0'
	   2) 朝当前元素的 上、下、左、右 四个方向开启下层递归,使用 或 连接 
	   	  【代表只需找到一条可行路径就直接返回,不再做后续 DFS 】
	   	  记录结果。
	   3) 还原当前矩阵元素。board[i][j] = work[k]
*/
class Solution {
    public boolean exist(char[][] board, String word) {
        char[] s = word.toCharArray();
        for(int i=0; i<board.length; i++){
            for(int j=0; j<board[0].length; j++){
                if(dfs(board, s, i, j, 0)) return true;
            }
        }
        return false;
    }
    boolean dfs(char[][] board, char[] word, int i, int j, int k){
        if(i<0 || i>board.length-1 || j<0 || j>board[0].length-1 || board[i][j]!=word[k]) return false;
        if(k == word.length-1) return true;
        board[i][j] = '\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] = word[k];
        return res;
    }
}


2. 剑指 Offer 13. 机器人的运动范围

原题链接
在这里插入图片描述
深度优先搜索

/*
	1. dfs, 题中说明从(0, 0)开始移动, 在本题中不需要上下左右四个方向搜索。
	   下、右两个方向就可以到达所有的坐标点。
	2. dfs函数
	   1) 参数(i,j,si,sj) i,j表示此时坐标,si,sj表示数位和
	   2) 返回值(结束条件)
	      0: 越界/已到达过/数位和大于k
	      1+dfs(): 可以到达此格子,到达数量+1
	3. 初始化一个二维数组用来标记已到达过的格子
       封装计算数位和的函数,便于计算
*/
class Solution {
    int m;
    int n;
    int k;
    boolean[][] visited;
    public int movingCount(int m, int n, int k) {
        this.m = m; this.n = n; this.k = k;
        this.visited = new boolean[m][n];
        return dfs(0,0,0,0);
    }
    int dfs(int i, int j, int si, int sj){
        if(i>=m || j>=n || visited[i][j] || si+sj>k) return 0;
        visited[i][j] = true;   // 已访问
        return 1+dfs(i+1,j,sum(i+1),sum(j))+dfs(i,j+1,sum(i),sum(j+1));
    }
    // 计算数位和
    int sum(int n){
        int s = 0;
        while(n != 0){
            s += n%10;
            n = n/10;
        }
        return s;
    }
}


3. 剑指 Offer 26. 树的子结构

原题链接
在这里插入图片描述在这里插入图片描述

/*
	1. 遍历二叉树A的节点,依次作为根节点与二叉树B比较
	2. 一个比较两棵树是否相同的函数recur(A,B)
	3. 前序遍历(根节点,左节点,右节点)
	   recur(A,B)
	   isSubStructure(A.left, B)
	   isSubStructure(A.right, B)
*/
class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        // 空树不是任意一个树的子结构
        if(A == null || B == null) return false;
        // 前序遍历
        return  recur(A,B) || isSubStructure(A.left, B) || isSubStructure(A.right, B);
    }
    //  A中是否有出现和B相同的结构和节点值
    boolean recur(TreeNode A, TreeNode B){
        if(B == null) return true;
        if(A == null || A.val != B.val) return false; 

        return recur(A.left, B.left) && recur(A.right, B.right);
    }
}


4. 剑指 Offer 34. 二叉树中和为某一值的路径

原题链接
在这里插入图片描述
在这里插入图片描述
先序遍历 + 路径记录

/*
	1. 创立两个链表。path存储当前路径,res存储满足条件的路径数组
	2. path满足条件则加入res中
	3. 满足条件即:到达叶子节点且路径和等于目标值
	4. 先序遍历
*/
class Solution {
    LinkedList<Integer> path = new LinkedList<>();
    LinkedList<List<Integer>> res = new LinkedList<>();
    public List<List<Integer>> pathSum(TreeNode root, int target) {
        recur(root,target);
        return res;
    }
    void recur(TreeNode root, int tar){
        if(root == null) return;
        path.add(root.val);
        tar -= root.val;
        if(tar == 0 && root.left == null && root.right == null) 
        // 避免直接添加 path 对象,拷贝了一个 path对象加入到res 
            res.add(new LinkedList(path));
        recur(root.left, tar);
        recur(root.right, tar);
        // 回溯将path中添加值删掉
        path.removeLast();
    }
}


5. 剑指 Offer 38. 字符串的排列

原题链接

在这里插入图片描述
全排列

/*
	1. 全排列
	2. 剪枝
	3. 使用的数据结构
*/
class Solution {
    List<String> res = new LinkedList<>();
    char[] c;
    public String[] permutation(String s) {
        c = s.toCharArray();
        dfs(0);
        return res.toArray(new String[res.size()]); // 转换为字符串数组
    }
    void dfs(int n){
        if(n == c.length-1){
            res.add(String.valueOf(c)); // String.valueOf 将c转换为字符串
            return;
        }
        // 用set来排除字符串数组中重复的字符
        HashSet<Character> set = new HashSet<>();
        for(int i=n; i<c.length; i++){
            if(set.contains(c[i])) continue;
            set.add(c[i]);
            swap(i,n);
            dfs(n+1);
            swap(i,n);
        }
    }
    void swap(int a, int b){
        char temp = c[a];
        c[a] = c[b];
        c[b] = temp;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值