2024.4.24力扣每日一题——感染二叉树需要的总时间

2024.4.24

      • 题目来源
      • 我的题解
        • 方法一 转化为图+广度优先搜索
        • 方法二 记录父节点+DFS
        • 方法三 一次遍历+树的直径

题目来源

力扣每日一题;题序:2385

我的题解

方法一 转化为图+广度优先搜索

先将树转换为图,然后进行广度优先搜索进行感染模拟

时间复杂度:O(n)
空间复杂度:O(n)

class Solution {
    int size=0;
    public int amountOfTime(TreeNode root, int start) {
        // List<Integer>[] g=createGraph(root);
        Map<Integer,List<Integer>> map=createMap(root);
        // System.out.println(map);
        Set<Integer> set=new HashSet<>();
        set.add(start);
        Queue<Integer> queue=new LinkedList<>();
        queue.offer(start);
        int res=0;
        while(!queue.isEmpty()){
            int sz=queue.size();
            for(int i=0;i<sz;i++){
                List<Integer> t=map.get(queue.poll());
                if(t==null)
                    continue;
                for(Integer e:t){
                    if(set.add(e))
                        queue.offer(e);
                }
            }
            res++;
        }
        //最后一层会多计算一次
        return res-1;
    }
    public Map<Integer,List<Integer>> createMap(TreeNode root){
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        Map<Integer,List<Integer>> map=new HashMap<>();
        while(!queue.isEmpty()){
            int sz=queue.size();
            for(int i=0;i<sz;i++){
                TreeNode tree=queue.poll();
                int t=tree.val;
                if(tree.left!=null){
                    addEdge(t,tree.left.val,map);
                    queue.offer(tree.left);
                }
                if(tree.right!=null){
                    addEdge(t,tree.right.val,map);
                    queue.offer(tree.right);
                }
            }
            size+=sz;
        }
        return map;
    }
    public void addEdge(int from,int to ,Map<Integer,List<Integer>> map){
        List<Integer> l1=map.getOrDefault(from,new ArrayList<>());
        List<Integer> l2=map.getOrDefault(to,new ArrayList<>());
        l1.add(to);
        l2.add(from);
        map.put(from,l1);
        map.put(to,l2);
    }
}
方法二 记录父节点+DFS

参考灵神的代码
首先从 root出发 DFS 这棵树,找到节点值为 start 的节点 startNode。DFS 的同时,用一个哈希表(或者数组)记录每个节点的父节点。
然后从 startNode出发 DFS 这棵树,求出二叉树的最大深度,即为答案(把 startNode 的深度当作 0)。注意除了递归左右儿子以外,还需要递归父节点。为避免重复访问节点,可以添加一个递归参数 from,表示当前节点是从节点 from 过来的,不去重复访问节点 from。

时间复杂度:O(n)
空间复杂度:O(n)

class Solution {
    private TreeNode startNode;
    private final Map<TreeNode, TreeNode> fa = new HashMap<>();

    public int amountOfTime(TreeNode root, int start) {
        dfs(root, null, start);
        return maxDepth(startNode, startNode);
    }

    private void dfs(TreeNode node, TreeNode from, int start) {
        if (node == null) {
            return;
        }
        fa.put(node, from); // 记录每个节点的父节点
        if (node.val == start) {
            startNode = node; // 找到 start
        }
        dfs(node.left, node, start);
        dfs(node.right, node, start);
    }

    private int maxDepth(TreeNode node, TreeNode from) {
        if (node == null) {
            return -1; // 注意这里是 -1,因为 start 的深度为 0
        }
        int res = -1;
        if (node.left != from) {
            res = Math.max(res, maxDepth(node.left, node));
        }
        if (node.right != from) {
            res = Math.max(res, maxDepth(node.right, node));
        }
        if (fa.get(node) != from) {
            res = Math.max(res, maxDepth(fa.get(node), node));
        }
        return res + 1;
    }
}
方法三 一次遍历+树的直径

参考灵神的代码
实质就是求 以start为直径端点的树的直径

时间复杂度:O(n)
空间复杂度:O(n)

class Solution {
    private int ans;

    public int amountOfTime(TreeNode root, int start) {
        dfs(root, start);
        return ans;
    }

    private int[] dfs(TreeNode node, int start) {
        if (node == null) {
            return new int[]{0, 0};
        }
        int[] leftRes = dfs(node.left, start);
        int[] rightRes = dfs(node.right, start);
        int lLen = leftRes[0], lFound = leftRes[1];
        int rLen = rightRes[0], rFound = rightRes[1];
        if (node.val == start) {
            // 计算子树 start 的最大深度
            // 注意这里和方法一的区别,max 后面没有 +1,所以算出的也是最大深度
            ans = Math.max(lLen, rLen);
            return new int[]{1, 1}; // 找到了 start
        }
        if (lFound == 1 || rFound == 1) {
            // 只有在左子树或右子树包含 start 时,才能更新答案
            ans = Math.max(ans, lLen + rLen); // 两条链拼成直径
            // 保证 start 是直径端点
            return new int[]{(lFound == 1 ? lLen : rLen) + 1, 1};
        }
        return new int[]{Math.max(lLen, rLen) + 1, 0};
    }
}

灵神是真的强!!!!!!!!!!

有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈????~

上一篇:Qt中常用对话框


下一篇:SpringBoot整合Mybatis