二叉树-02

1. 路径总和 II

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

叶子节点 是指没有子节点的节点。

真题点击此处:113. 路径总和 II

1.1 深度优先搜索

解题方法:

  1. 定义一个ans列表用于存储所有符合条件的路径,定义一个path列表用于存储当前遍历的路径。
  2. 创建一个深度优先搜索函数dfs,该函数接收两个参数:当前节点root和目标和targetSum。
  3. 在dfs函数中,首先判断当前节点是否为空,如果为空则直接返回。
  4. 在path列表中添加当前节点的值,并将目标和减去当前节点的值。
  5. 如果当前节点为叶子节点并且目标和已经减少至0,则将当前path列表拷贝到ans列表中,表示找到了一条符合条件的路径。
  6. 递归调用dfs函数遍历当前节点的左子树和右子树。
  7. 在递归调用结束后,将path列表中的最后一个元素移除,以回退到上一层节点继续遍历。
  8. 在主函数中调用dfs函数进行深度优先搜索,并返回最终的结果。

以下为代码实现:

class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
        ans=[]
        path=[]

        def dfs(root, targetSum):
            if root is None:
                return 
            
            path.append(root.val)
            targetSum -= root.val

            if not root.left and not root.right and targetSum == 0:
                ans.append(path[:])

            dfs(root.left, targetSum)
            dfs(root.right, targetSum)

            path.pop()

        dfs(root, targetSum)
        return ans

时间复杂度:O(N2),其中 N 是树的节点数。在最坏情况下,树的上半部分为链状,下半部分为完全二叉树,并且从根节点到每一个叶子节点的路径都符合题目要求。此时,路径的数目为 O(N),并且每一条路径的节点个数也为 O(N),因此要将这些路径全部添加进答案中,时间复杂度为 O(N2)。

空间复杂度:O(N),其中 N 是树的节点数。空间复杂度主要取决于栈空间的开销,栈中的元素个数不会超过树的节点数。

1.2 广度优先搜索

解题方法:

  1. 首先定义了一个成员变量ans,用于存储所有符合条件的路径,同时定义了一个map用于记录节点以及其对应的父节点。
  2. 创建了一个pathSum方法,接收二叉树的根节点和目标和作为参数。
  3. 首先对根节点进行空值检查,如果根节点为空则直接返回ans。
  4. 接着创建两个队列queueNode和queueSum,分别用于存储当前节点和从根节点到当前节点的路径和。
  5. 将根节点和路径和0分别加入队列中。
  6. 使用while循环遍历队列,直到队列为空为止。
  7. 在循环中,首先弹出队列中的节点和路径和,并计算出当前节点的累计和rec。
  8. 如果当前节点是叶子节点且累计和等于目标和,则调用getPath方法获取路径。
  9. 将当前节点的左右子节点加入队列,并更新它们的累计和。
  10. getPath方法用于根据map中记录的父节点信息,从叶子节点向上回溯得到完整的路径,并将路径添加到ans中。

以下为代码实现:

class Solution {
    List<List<Integer>> ans = new LinkedList<List<Integer>>();
    Map<TreeNode, TreeNode> map = new HashMap<TreeNode, TreeNode>();

    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        if(root == null){
            return ans;
        }

        Queue<TreeNode> queueNode = new LinkedList<>();
        Queue<Integer> queueSum = new LinkedList<>();
        queueNode.offer(root);
        queueSum.offer(0);

        while(!queueNode.isEmpty()){
            TreeNode node = queueNode.poll();
            int rec = queueSum.poll() + node.val;

            if(node.left == null && node.right == null){
                if(targetSum == rec){
                    getPath(node);
                }
            }
            
            if(node.left != null){
                map.put(node.left, node);
                queueNode.offer(node.left);
                queueSum.offer(rec);
            }

            if(node.right != null){
                map.put(node.right, node);
                queueNode.offer(node.right);
                queueSum.offer(rec);
            }
        }
        return ans;
    }

    public void getPath(TreeNode node){
        List<Integer> temp = new LinkedList<Integer>();
        while(node != null){
            temp.add(node.val);
            node = map.get(node);
        }
        Collections.reverse(temp);
        ans.add(new LinkedList<Integer>(temp));
    }
}

时间复杂度:O(N2),其中 N 是树的节点数。分析思路与方法一相同。

空间复杂度:O(N),其中 NNN 是树的节点数。空间复杂度主要取决于哈希表和队列空间的开销,哈希表需要存储除根节点外的每个节点的父节点,队列中的元素个数不会超过树的节点数。

2. 求根节点到叶节点数字之和

给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。
每条从根节点到叶节点的路径都代表一个数字:

例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。
计算从根节点到叶节点生成的 所有数字之和 。

叶节点 是指没有子节点的节点。

真题点击此处:129. 求根节点到叶节点数字之和

2.1 深度优先搜索

解题方法:

  1. 定义了一个sumNumbers方法,接收二叉树的根节点作为参数。
  2. 在sumNumbers方法中定义了一个内部的dfs函数,用于实现深度优先搜索。
  3. dfs函数接收两个参数:当前节点和从根节点到当前节点的路径数字之和。
  4. 如果当前节点为空,则直接返回0。
  5. 计算当前节点的路径数字之和,即prevTotal * 10 + root.val。
  6. 如果当前节点是叶子节点,则返回路径数字之和。
  7. 如果当前节点不是叶子节点,则递归地对左右子节点调用dfs函数,并将当前节点的路径数字之和传入。
  8. 将左右子节点的计算结果相加并返回给上一层递归调用。

以下为代码实现:

class Solution:
    def sumNumbers(self, root: TreeNode) -> int:
        def dfs(root: TreeNode, prevTotal: int) -> int:
            if not root:
                return 0
            total = prevTotal * 10 + root.val
            if not root.left and not root.right:
                return total
            else:
                return dfs(root.left, total) + dfs(root.right, total)

        return dfs(root, 0)

时间复杂度:O(n),其中 n 是二叉树的节点个数。对每个节点访问一次。

空间复杂度:O(n),其中 n 是二叉树的节点个数。空间复杂度主要取决于递归调用的栈空间,递归栈的深度等于二叉树的高度,最坏情况下,二叉树的高度等于节点个数,空间复杂度为O(n)。

2.2 广度优先搜索

解题方法:

  1. 首先对根节点进行空值检查,如果根节点为空则直接返回0。
  2. 定义一个整型变量sum用于存储最终求和结果。
  3. 创建两个队列nodeQueue和numQueue,分别用于存储当前节点和从根节点到当前节点的路径数字之和。
  4. 将根节点和其值分别加入两个队列中。
  5. 使用while循环遍历节点队列,直到队列为空为止。
  6. 在循环中,首先弹出队列中的节点和路径数字之和,并将其分别存储在node和num变量中。
  7. 如果当前节点是叶子节点,则将num累加到sum中。
  8. 如果当前节点不是叶子节点,则将其左右子节点加入队列中,并更新它们的路径数字之和。
  9. 在方法结束时,返回求和结果sum。

以下为代码实现:

class Solution {
    public int sumNumbers(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int sum = 0;
        Queue<TreeNode> nodeQueue = new LinkedList<TreeNode>();
        Queue<Integer> numQueue = new LinkedList<Integer>();
        nodeQueue.offer(root);
        numQueue.offer(root.val);
        while (!nodeQueue.isEmpty()) {
            TreeNode node = nodeQueue.poll();
            int num = numQueue.poll();
            TreeNode left = node.left, right = node.right;
            if (left == null && right == null) {
                sum += num;
            } else {
                if (left != null) {
                    nodeQueue.offer(left);
                    numQueue.offer(num * 10 + left.val);
                }
                if (right != null) {
                    nodeQueue.offer(right);
                    numQueue.offer(num * 10 + right.val);
                }
            }
        }
        return sum;
    }
}

时间复杂度:O(n),其中 n 是二叉树的节点个数。对每个节点访问一次。

空间复杂度:O(n,其中 n 是二叉树的节点个数。空间复杂度主要取决于队列,每个队列中的元素个数不会超过 n。

3. 统计二叉树中好节点的数目

给你一棵根为 root 的二叉树,请你返回二叉树中好节点的数目。

「好节点」X 定义为:从根到该节点 X 所经过的节点中,没有任何节点的值大于 X 的值。

真题点击此处:1448. 统计二叉树中好节点的数目

3.1 深度优先搜索

解题思路:

  1. 定义了一个goodNodes方法,接收二叉树的根节点作为参数。
  2. 在goodNodes方法中定义了一个内部的dfs函数,用于实现深度优先搜索。
  3. dfs函数接收两个参数:当前节点和当前路径上的最大值mx。
  4. 如果当前节点为空,则直接返回。
  5. 使用nonlocal关键字声明ans变量为非局部变量,用于记录"好节点"的数量。
  6. 如果当前节点的值大于等于mx,则将"好节点"数量加1,并更新最大值mx为当前节点的值。
  7. 递归地对左右子节点调用dfs函数,并传入更新后的最大值mx。
  8. 在方法结束时,返回"好节点"的数量ans。

以下为代码实现:

class Solution:
    def goodNodes(self, root: TreeNode) -> int:
        ans = 0
        def dfs(root, mx):
            if root is None:
                return

            nonlocal ans
            if root.val >= mx:
                ans += 1
                mx = root.val

            dfs(root.left, mx)
            dfs(root.right, mx)

        dfs(root, float('-inf'))
        return ans

时间复杂度:O(n),其中 n 为二叉树中的节点个数。在深度优先遍历的过程中,每个节点只会被遍历一次。

空间复杂度:O(n)。由于我们使用递归来实现深度优先遍历,因此空间复杂度的消耗主要在栈空间,取决于二叉树的高度,最坏情况下二叉树的高度为 O(n)。

3.2 广度优先搜索

解题思路:

  1. 定义了一个goodNodes方法,接收二叉树的根节点作为参数。
  2. 首先初始化一个整型变量ans用于记录"好节点"的数量,并对根节点进行空值检查。
  3. 创建两个队列queueNode和queueVal,分别用于存储当前节点和当前路径上的最大值。
  4. 将根节点和Integer.MIN_VALUE(负无穷)加入两个队列中。
  5. 使用while循环遍历节点队列,直到队列为空为止。
  6. 在循环中,首先从两个队列中分别弹出当前节点node和当前路径上的最大值val。
  7. 如果当前节点的值大于等于当前路径上的最大值,则将"好节点"数量ans加1,并更新最大值val为当前节点的值。
  8. 如果当前节点有左子节点,则将其加入队列,并将当前路径上的最大值val加入到值队列中。
  9. 如果当前节点有右子节点,则将其加入队列,并将当前路径上的最大值val加入到值队列中。
  10. 最终返回"好节点"的数量ans。

以下为代码实现:

class Solution {
    public int goodNodes(TreeNode root) {
        int ans = 0;
        if(root == null){
            return 0;
        }

        Queue<TreeNode> queueNode = new LinkedList<>();
        Queue<Integer> queueVal = new LinkedList<>();
        queueNode.offer(root);
        queueVal.offer(Integer.MIN_VALUE);

        while(!queueNode.isEmpty()){
            TreeNode node = queueNode.poll();
            int val = queueVal.poll();

            if(node.val >= val){
                ans++;
                val = node.val;
            }

            if(node.left != null){
                queueNode.offer(node.left);
                queueVal.offer(val);
            }

            if(node.right != null){
                queueNode.offer(node.right);
                queueVal.offer(val);
            }
        }
        return ans; 
    }
}

时间复杂度:O(N),采用广度优先搜索的方式遍历整棵树,其中N表示二叉树中节点的数量。

空间复杂度:O(N),使用了两个队列来辅助遍历节点,因此额外空间复杂度取决于队列中元素的数量。

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值