leetcode513. 找树左下角的值:层序遍历中的深度与顺序控制之道
一、题目深度解析与核心诉求
在二叉树的众多问题中,寻找最深层最左节点的值是一个兼具趣味性与代表性的问题。题目要求我们在给定的二叉树中,找到深度最大的那一层中最左边的节点值。如果存在多个最深层,只需返回最左边节点的值即可。
这个问题的核心难点在于:
- 如何准确判断树的最深层
- 如何在最深层中找到最左边的节点
- 如何高效地实现这一查找过程
为了更好地理解问题,我们来看一个示例:
对于如下二叉树:
1/ \2 3/ / \
4 5 6/7
最深层是第3层(假设根节点为第1层),该层最左节点是7,因此返回7。
二、迭代解法的核心实现:层序遍历的巧妙应用
针对这个问题,我们可以使用层序遍历(广度优先搜索)的方法来解决。这种方法的优势在于可以按层处理节点,天然适合处理与深度相关的问题。下面是实现代码:
/*** Definition for a binary tree node.* public 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 int findBottomLeftValue(TreeNode root) {int res = 0;Deque<TreeNode> cur = new ArrayDeque<>();TreeNode temp = root;cur.offer(temp);int cnt = 0;while (!cur.isEmpty()) {int len = cur.size();cnt = 0;while (len > 0) {temp = cur.poll();if (cnt == 0) {res = temp.val;}if (temp.left != null) {cur.offer(temp.left);}if (temp.right != null) {cur.offer(temp.right);}cnt++;len--;}}return res;}
}
三、核心问题解析:如何判断最深层最左节点
1. 层序遍历与深度判断
层序遍历的一个重要特点是按层处理节点,每处理完一层,才会处理下一层。在这个算法中,我们使用一个队列来实现层序遍历。每次从队列中取出当前层的所有节点,处理完后,队列中剩下的就是下一层的节点。
具体来说:
- 初始时,将根节点加入队列
- 每次循环处理一层的节点:
- 首先获取当前队列的大小,这个大小就是当前层的节点数
- 然后依次取出当前层的所有节点,处理它们的子节点
- 处理完当前层后,队列中剩下的就是下一层的节点
这种处理方式使得我们可以很方便地按层处理节点,从而能够准确地判断当前处理的是哪一层。
2. 最左节点的判断
在每一层的处理中,我们需要找到最左边的节点。由于队列是先进先出(FIFO)的结构,因此每一层中最先取出的节点就是该层最左边的节点。
具体实现中,我们使用一个计数器cnt
来记录当前取出的是当前层的第几个节点。当cnt=0
时,说明取出的是当前层的第一个节点,也就是最左边的节点,此时将该节点的值赋给结果变量res
。
3. 最深层的确定
由于层序遍历是按层处理的,因此最后处理的一层就是最深层。在处理最深层时,我们取出的第一个节点就是最深层最左的节点,其值会被赋给res
,最终返回这个值。
四、算法流程模拟与详细解释
以之前的示例二叉树为例,我们来模拟一下算法的执行过程:
二叉树结构:
1/ \2 3/ / \
4 5 6/7
-
初始状态:
- 队列中有节点1
res=0
cnt=0
-
第一次循环(处理第1层):
len=1
(当前层节点数)- 进入内层循环,
len>0
:- 取出节点1,
cnt=0
,res=1
- 将节点1的左子节点2和右子节点3加入队列
cnt=1
,len=0
- 取出节点1,
- 内层循环结束,队列中有节点2、3
-
第二次循环(处理第2层):
len=2
(当前层节点数)- 进入内层循环,
len>0
:- 取出节点2,
cnt=0
,res=2
- 将节点2的左子节点4加入队列(右子节点为空)
cnt=1
,len=1
- 取出节点3,
cnt=1
,不更新res
- 将节点3的左子节点5和右子节点6加入队列
cnt=2
,len=0
- 取出节点2,
- 内层循环结束,队列中有节点4、5、6
-
第三次循环(处理第3层):
len=3
(当前层节点数)- 进入内层循环,
len>0
:- 取出节点4,
cnt=0
,res=4
- 节点4的左右子节点均为空,不加入队列
cnt=1
,len=2
- 取出节点5,
cnt=1
,不更新res
- 将节点5的左子节点7加入队列(右子节点为空)
cnt=2
,len=1
- 取出节点6,
cnt=2
,不更新res
- 节点6的左右子节点均为空,不加入队列
cnt=3
,len=0
- 取出节点4,
- 内层循环结束,队列中有节点7
-
第四次循环(处理第4层):
len=1
(当前层节点数)- 进入内层循环,
len>0
:- 取出节点7,
cnt=0
,res=7
- 节点7的左右子节点均为空,不加入队列
cnt=1
,len=0
- 取出节点7,
- 内层循环结束,队列为空
-
循环结束,返回
res=7
,即最深层最左节点的值。
五、算法复杂度分析
- 时间复杂度:O(n),其中n是树的节点数。因为我们需要访问每个节点一次。
- 空间复杂度:O(m),其中m是树的最大宽度。在层序遍历中,队列的大小最多为树的最大宽度,也就是同一层的节点数。
六、算法的核心优势与适用场景
1. 优势
- 层序遍历的方式使得我们可以很方便地按层处理节点,准确判断最深层
- 利用队列的FIFO特性,轻松找到每一层的最左节点
- 算法逻辑清晰,实现简单,时间复杂度和空间复杂度都比较理想
2. 适用场景
- 当需要按层处理二叉树节点时
- 当问题涉及到树的深度或某一层的节点时
- 当需要找到某一层的特定位置节点时
七、总结:层序遍历的深度与顺序控制
在寻找树的左下角值的问题中,我们利用层序遍历的特性,巧妙地解决了深度判断和最左节点查找的问题。这种方法的核心在于:
- 利用队列实现层序遍历,按层处理节点
- 通过记录每一层的节点数,准确判断当前处理的是哪一层
- 利用队列的FIFO特性,第一个取出的节点即为当前层的最左节点
- 由于层序遍历是按层进行的,最后处理的一层就是最深层,该层的最左节点即为所求
这种方法不仅解决了当前的问题,也为解决其他类似的二叉树问题提供了思路。例如,寻找最深层最右节点、计算某一层的节点和等问题,都可以基于层序遍历的思想来解决。
通过这个问题,我们可以看到,合理利用数据结构的特性(如队列的FIFO特性),并结合问题的特点(按层处理),可以设计出高效、简洁的算法。这也是解决算法问题的关键所在。