zoukankan      html  css  js  c++  java
  • 【40讲系列8】广度优先搜索、深度优先搜索

    一、理论

    BFS用到队列,只有非递归写法。

    应用到树时,不需要标记该节点是否走过。而应用到图结构时,需要标记某个节点是否已被访问过。

    DFS需要用栈,有递归和非递归写法,推荐使用递归写法

    二、典型例题

    ①:二叉树层次遍历(LC102、剑指22.从上往下打印二叉树

    方法1:树的BFS,需要用队列。   (方法1见剑指22)

    方法2:还可以用DFS,为了让递归的过程中同一层的节点放到同一个列表中,在递归时要记录每个节点的深度 level。递归到新节点要把该节点放入 level 对应列表的末尾(当遍历到一个新的深度 level,而最终结果 res 中还没有创建 level 对应的列表时,要在res新建一个)

    方法2(DFS):

    class Solution {
        public List<List<Integer>> levelOrder(TreeNode root) {
            List<List<Integer>> res = new ArrayList<>();
            if (root != null){
                dfs(res,root,0);
            }
            return res;
        }
        private void dfs(List<List<Integer>> res, TreeNode root, int level){
            if (root == null) return;
    
            if (res.size() - 1 < level){
                res.add(new ArrayList<>());
            }
            res.get(level).add(root.val);
    
            dfs(res,root.left,level+1);
            dfs(res,root.right,level+1);
        }
    }

     

    ②:二叉树最大深度(LC104、剑指38.二叉树的深度

    方法1:递归。树的深度=max(左子树深度,右子树深度)+1

    方法2:BFS。层序遍历时记录层数即可

    方法3:DFS,模仿上一题的写法。

    方法1和方法2 见剑指38.二叉树的深度

    方法3(DFS):

    class Solution {
        int max = 0; // 全局,记录
        public int maxDepth(TreeNode root) {
            if (root == null) return 0;
            dfs(root,1);
            return max;
        }
        private void dfs(TreeNode root, int level){
            if (root == null) return;
            /*if (max < level){  // 每次到了新节点都判断一下
                max = level;
            }*/
            if (root.left == null && root.right == null){ // 到了叶子节点再判断并更新max
                max = max < level ? level : max;
            }
            dfs(root.left,level + 1);
            dfs(root.right,level + 1);
        }
    }

    ③:二叉树最小深度(LC111)

    方法1:递归。需要单独处理没有左子树或没有右子树的特殊情况

    方法2:BFS

    方法3:DFS

    class Solution {
    
        int min = Integer.MAX_VALUE;
        public int minDepth(TreeNode root) {
            // 方法1. 递归
            if (root == null) return 0;
            int left = minDepth(root.left);
            int right = minDepth(root.right);
            // 如果left和right都为0,说明是叶子节点,返回1即可,
            // 如果left和right有一个为0,说明其中一个子节点为空,此时返回它另一个子节点的最小深度+1即可。
            // 如果left和right都不为0,说明左右孩子都不为空,返回最小深度的+1即可。
            return (left == 0 || right == 0) ? 1 + left + right : Math.min(left,right) + 1;
    
            // 方法2. BFS
            /*
            if (root == null) return 0;
            int min = 0;
            Queue<TreeNode> queue = new LinkedList<>();
            queue.offer(root);
            while (!queue.isEmpty()){
                int levelNums = queue.size();
                min++;
                for (int i = 0; i < levelNums; i++) {
                    TreeNode cur = queue.poll();
                    if (cur.left == null && cur.right == null) // 最先找到的叶子节点的深度一定最小
                        return min;
                    if (cur.left != null) queue.offer(cur.left);
                    if (cur.right != null) queue.offer(cur.right);
                }
            }
            return -1;
            */
            // 方法3 :DFS
            /*
            if (root == null) return 0;
            dfs(root, 1);
            return min;
            */
        }
        private void dfs(TreeNode root,int level){
            if (root == null) return;
            if (root.left == null && root.right == null){ // 每次到叶子节点时 更新min
                min = level < min ? level : min;
            }
            dfs(root.left,level+1);
            dfs(root.right,level+1);
        }
    }

    ☆☆☆④:生成所有可能的有效括号组合(LC22)

    思路:深搜回溯。每步要么增加一个左括号,要么增加一个右括号,是一二叉的选择,所以可以用深搜。但是并不是每个分支都符合要求,如果右括号使用的比左括号多的话就已经不是正确括号了,没必要继续dfs这个分支了,所以加if来剪枝。

    class Solution {
        List<String> res = new ArrayList<>();
        public List<String> generateParenthesis(int n) {
            if (n == 0) return res;
    
            dfs(0,0, n,"");
            return res;
        }
        private void dfs(int left, int right,int n, String str){
            if (left == n && right == n){ // 左右括号都用完了
                res.add(str);
                return;
            }
            if (left < n){
                dfs(left + 1,right,n,str + "(");
            }
            // 左括号比右括号多才可以匹配,此时才放右括号。  相当于剪枝
            if (right < n && left > right){
                dfs(left,right + 1,n,str + ")");
            }
        }
    }

     

  • 相关阅读:
    Eclipse/MyEclip中使用复制粘贴功能很卡
    ActiveMQ使用教程
    ActiveMQ常见消息类型
    推荐12个最好的 JavaScript 图形绘制库
    eclipse 打包springboot成jar
    CentOS 7下ELK(6.2.4)++LogStash+Filebeat+Log4j日志集成环境搭建
    elasticsearch启动常见错误
    使用haproxy实现负载均衡集群
    linux配置mysql5.6 主主复制同步
    力扣No.102 二叉树的层次遍历
  • 原文地址:https://www.cnblogs.com/HuangYJ/p/14018307.html
Copyright © 2011-2022 走看看