#112
简单
低频
广度优先遍历路径总和
这是一道围绕树、二叉树、图展开的高频练习。建议先掌握「广度优先遍历」这套写法,再结合下方步骤讲解理解状态维护、边界处理和复杂度取舍。
树
二叉树
图
题目分析
这道题表面上是在处理「路径总和」,但先要想清楚题目到底让你返回什么,以及过程中哪些约束必须一直满足。从题型上看,它主要在考 树、二叉树 这些能力。这类题通常要按层或按顺序推进,遍历过程中往往还要把附加信息一起带着走。只有先把题目要求翻译成人话,后面的推荐代码才是在实现思路,而不是直接给答案。
接下来怎么看推荐代码: 带着这个理解再看推荐代码时,重点观察这条主线:BFS 遍历节点时同步携带从根到当前节点的路径和,到叶子时检查是否等于目标值。
推荐代码
推荐解法:广度优先遍历
时间复杂度: O(n)
空间复杂度: O(n)
核心思路: BFS 遍历节点时同步携带从根到当前节点的路径和,到叶子时检查是否等于目标值。
// 二叉树节点定义
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
// 如果根节点为空,直接返回 false
if (root == null) {
return false;
}
// 创建两个队列:一个存储节点,一个存储对应的路径和
Queue<TreeNode> queNode = new LinkedList<TreeNode>();
Queue<Integer> queVal = new LinkedList<Integer>();
// 将根节点和根节点的值加入队列
queNode.offer(root);
queVal.offer(root.val);
// 当队列不为空时,继续处理
while (!queNode.isEmpty()) {
// 取出当前节点和对应的路径和
TreeNode now = queNode.poll();
int temp = queVal.poll();
// 如果当前节点是叶子节点
if (now.left == null && now.right == null) {
// 检查路径和是否等于目标值
if (temp == sum) {
return true;
}
// 继续处理其他路径
continue;
}
// 如果左子节点存在,将左子节点和更新后的路径和加入队列
if (now.left != null) {
queNode.offer(now.left);
queVal.offer(now.left.val + temp);
}
// 如果右子节点存在,将右子节点和更新后的路径和加入队列
if (now.right != null) {
queNode.offer(now.right);
queVal.offer(now.right.val + temp);
}
}
// 没有找到符合条件的路径
return false;
}
}结构化讲解
面试时怎么讲
开场思路
这题我会用 BFS,把节点和当前路径和一起放进队列里。
核心过程
- 根节点入队时同步记录根值作为路径和。
- 每次出队都检查当前节点是否是叶子且和等于目标。
- 若没命中,就把左右孩子和更新后的路径和继续入队。
- 遍历结束仍未命中则返回 false。
复杂度总结
时间复杂度 O(n),空间复杂度 O(n)。
面试补一句:这题最该强调的是:队列里存的不只是节点,还包括状态。
核心思路
路径总和不一定非得递归。只要在队列里把“节点”和“当前路径和”一起存起来,BFS 同样能在线判断是否存在合法根到叶路径。
步骤讲解
1
队列同时保存节点和路径和
根节点入队时,把它当前路径和也一并记录。
为什么这样做:后续每个节点都需要知道从根走到它的累计和。
对应代码提示:queue.offer(new Pair(root, root.val));
2
到叶子时判断是否命中目标
如果当前节点是叶子,并且路径和等于 target,就可以返回 true。
为什么这样做:题目只接受根到叶路径,不能在中间节点提前判定。
对应代码提示:if (node.left == null && node.right == null && sum == targetSum) return true;
3
扩展孩子时更新路径和
把左右孩子入队时,把新的路径和也同步累加进去。
为什么这样做:每个孩子节点的路径和都依赖父节点路径和。
对应代码提示:queue.offer(new Pair(node.left, sum + node.left.val));
易错点
只判断到任意节点的路径和
题目要求的是根到叶,不是任意前缀路径。
正确理解:必须同时满足“叶子节点 + 路径和命中”。
队列里只存节点不存路径和
这样后续判断时还得重新回溯父路径。
正确理解:把节点和累计和一起入队。
左右孩子路径和复用了同一变量
容易在更新顺序上写乱。
正确理解:给每个孩子都单独算新和再入队。
复杂度与适用判断
时间复杂度:O(n)
空间复杂度:O(n)
比其他方案更好在哪里:比 DFS 递归更适合展示“节点 + 状态”一起遍历的思路。
适用判断:树遍历题若需要沿路携带累计信息,BFS/DFS 都可以搭配状态一起走。
额外提醒
- 叶子条件和目标和条件必须同时成立。
动画演示
如果你还没完全看懂,可以展开动画演示。
广度优先遍历动画演示
想看这段解法是怎么一步步运行的,可以展开动画继续观察。
广度优先遍历动画演示
想看这段解法是怎么一步步运行的,可以展开动画继续观察。
准备就绪 - 输入二叉树(层序遍历,用逗号分隔)和目标总和,然后点击开始
动画演示仅在桌面端提供,移动端请优先阅读推荐代码与结构化讲解。