二叉树题目:路径总和 II

文章目录

题目

标题和出处

标题:路径总和 II

出处:113. 路径总和 II

难度

4 级

题目描述

要求

给你二叉树的根结点 root \texttt{root} root 和一个表示目标和的整数 targetSum \texttt{targetSum} targetSum,返回所有的满足路径上结点值总和等于目标和 targetSum \texttt{targetSum} targetSum 的从根结点到叶结点的路径。每条路径应该以结点值列表的形式返回,而不是结点的引用。

示例

示例 1:

输入: root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22 \texttt{root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22} root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22

输出: [[5,4,11,2],[5,8,4,5]] \texttt{[[5,4,11,2],[5,8,4,5]]} [[5,4,11,2],[5,8,4,5]]

解释:有两条路径的结点值总和等于 targetSum \texttt{targetSum} targetSum:
5 + 4 + 11 + 2 = 22 \texttt{5 + 4 + 11 + 2 = 22} 5 + 4 + 11 + 2 = 22
5 + 8 + 4 + 5 = 22 \texttt{5 + 8 + 4 + 5 = 22} 5 + 8 + 4 + 5 = 22

示例 2:

输入: root = [1,2,3], targetSum = 5 \texttt{root = [1,2,3], targetSum = 5} root = [1,2,3], targetSum = 5

输出: [] \texttt{[]} []

示例 3:

输入: root = [1,2], targetSum = 0 \texttt{root = [1,2], targetSum = 0} root = [1,2], targetSum = 0

输出: [] \texttt{[]} []

数据范围

  • 树中结点数目在范围 [0, 5000] \texttt{[0, 5000]} [0, 5000] 内
  • -1000 ≤ Node.val ≤ 1000 \texttt{-1000} \le \texttt{Node.val} \le \texttt{1000} -1000≤Node.val≤1000
  • -1000 ≤ targetSum ≤ 1000 \texttt{-1000} \le \texttt{targetSum} \le \texttt{1000} -1000≤targetSum≤1000

前言

这道题是「路径总和」的进阶,要求返回所有的结点值总和等于目标和的从根结点到叶结点的路径。这道题也可以使用深度优先搜索和广度优先搜索得到答案,在搜索过程中需要维护路径。

解法一

思路和算法

如果二叉树为空,则不存在结点值总和等于目标和的路径。只有当二叉树不为空时,才可能存在结点值总和等于目标和的路径,需要从根结点开始寻找路径。

从根结点开始深度优先搜索,在遍历每一个结点的同时需要维护从根结点到当前结点的路径以及剩余目标和,将原目标和减去当前结点值即可得到剩余目标和。当访问到叶结点时,如果剩余目标和为 0 0 0,则从根结点到当前叶结点的路径即为结点值总和等于目标和的路径,将该路径添加到结果列表中。

由于深度优先搜索过程中维护的路径会随着访问到的结点而变化,因此当找到结点值总和等于目标和的路径时,需要新建一个路径对象添加到结果列表中,避免后续搜索过程中路径变化对结果造成影响。

代码

java 复制代码
class Solution {
    List<List<Integer>> paths = new ArrayList<List<Integer>>();
    List<Integer> path = new ArrayList<Integer>();

    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        dfs(root, targetSum);
        return paths;
    }

    public void dfs(TreeNode node, int targetSum) {
        if (node == null) {
            return;
        }
        path.add(node.val);
        targetSum -= node.val;
        if (node.left == null && node.right == null && targetSum == 0) {
            paths.add(new ArrayList<Integer>(path));
        }
        dfs(node.left, targetSum);
        dfs(node.right, targetSum);
        path.remove(path.size() - 1);
    }
}

复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2),其中 n n n 是二叉树的结点数。每个结点都被访问一次,最坏情况下每次将路径添加到结果中的时间是 O ( n ) O(n) O(n),因此总时间复杂度是 O ( n 2 ) O(n^2) O(n2)。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是递归调用的栈空间以及深度优先搜索过程中存储路径的空间,取决于二叉树的高度,最坏情况下二叉树的高度是 O ( n ) O(n) O(n)。注意返回值不计入空间复杂度。

解法二

思路和算法

使用广度优先搜索寻找结点值总和等于目标和的路径时,首先找到这些路径对应的叶结点,然后得到从叶结点到根结点的路径,将路径翻转之后即可得到相应的路径。

为了得到从叶结点到根结点的路径,需要使用哈希表存储每个结点的父结点,在广度优先搜索的过程中即可将每个结点和父结点的关系存入哈希表中。

广度优先搜索需要维护两个队列,分别存储结点与对应的结点值总和。广度优先搜索的过程中,如果遇到一个叶结点对应的结点值总和等于目标和,则找到一条结点值总和等于目标和的路径,利用哈希表中存储的父结点信息得到从当前叶结点到根结点的路径,然后将路径翻转,添加到结果中。

代码

java 复制代码
class Solution {
    List<List<Integer>> paths = new ArrayList<List<Integer>>();
    Map<TreeNode, TreeNode> parents = new HashMap<TreeNode, TreeNode>();

    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return paths;
        }
        Queue<TreeNode> nodeQueue = new ArrayDeque<TreeNode>();
        Queue<Integer> sumQueue = new ArrayDeque<Integer>();
        nodeQueue.offer(root);
        sumQueue.offer(root.val);
        while (!nodeQueue.isEmpty()) {
            TreeNode node = nodeQueue.poll();
            int sum = sumQueue.poll();
            TreeNode left = node.left, right = node.right;
            if (left == null && right == null && sum == targetSum) {
                paths.add(getPath(node));
            }
            if (left != null) {
                parents.put(left, node);
                nodeQueue.offer(left);
                sumQueue.offer(sum + left.val);
            }
            if (right != null) {
                parents.put(right, node);
                nodeQueue.offer(right);
                sumQueue.offer(sum + right.val);
            }
        }
        return paths;
    }

    public List<Integer> getPath(TreeNode node) {
        List<Integer> path = new ArrayList<Integer>();
        while (node != null) {
            path.add(node.val);
            node = parents.get(node);
        }
        Collections.reverse(path);
        return path;
    }
}

复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2),其中 n n n 是二叉树的结点数。每个结点都被访问一次,最坏情况下每次将路径添加到结果中的时间是 O ( n ) O(n) O(n),因此总时间复杂度是 O ( n 2 ) O(n^2) O(n2)。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是哈希表和队列空间,哈希表需要存储每个结点的父结点,需要 O ( n ) O(n) O(n) 的空间,两个队列内元素个数都不超过 n n n。注意返回值不计入空间复杂度。

相关推荐
袁气满满~_~1 小时前
LeetCode:617、合并二叉树
算法·leetcode·二叉树
在肯德基吃麻辣烫3 天前
【笔试训练】给一个数组构建二叉树|从前序遍历与中序遍历构建二叉树|二叉树中的最大路径和
二叉树·最大路径和
星沁城4 天前
236. 二叉树的最近公共祖先
java·数据结构·leetcode·二叉树
mikey棒棒棒5 天前
二叉树的遍历与构造
算法·二叉树
好易学·数据结构6 天前
可视化图解算法36: 序列化二叉树-I(二叉树序列化与反序列化)
数据结构·算法·leetcode·二叉树·力扣·序列化·牛客
海码00712 天前
【Hot 100】94. 二叉树的中序遍历
数据结构·c++·算法·二叉树·hot100
想不明白的过度思考者18 天前
初识数据结构——二叉树从基础概念到实践应用
数据结构·二叉树
好易学·数据结构21 天前
可视化图解算法:二叉树的最大深度(高度)
数据结构·算法·二叉树·最大高度·最大深度·二叉树高度·二叉树深度
好易学数据结构23 天前
可视化图解算法:按之字形顺序打印二叉树( Z字形、锯齿形遍历)
数据结构·算法·leetcode·面试·二叉树·力扣·笔试·遍历·二叉树遍历·牛客网·层序遍历·z·z字形遍历·锯齿形遍历
carpell1 个月前
二叉树实战篇2
python·二叉树·数据结构与算法