LeetCode 513. 找树左下角的值
问题描述
给定一个二叉树的根节点 root,请找出该二叉树的 最底层最左边 节点的值。
示例:
输入: root = [2,1,3]
输出: 1
输入: root = [1,2,3,4,null,5,6,null,null,7]
输出: 7
输入: root = [1,null,2,null,3]
输出: 3
算法思路
关键:
- 最底层:深度最大的层
- 最左边:在最底层中,从左到右第一个节点
方法:
- 层序遍历(BFS):按层遍历,最后一层的第一个节点就是答案
- 深度优先遍历(DFS):记录最大深度和对应的左节点值
- 右到左层序遍历:从右到左遍历每层,最后一个访问的节点就是答案
代码实现
方法一:层序遍历
import java.util.*;
/**
* Definition for a binary tree node.
*/
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 {
/**
* 层序遍历:按层遍历,记录每层第一个节点
*
* @param root 二叉树根节点
* @return 最底层最左边节点的值
*/
public int findBottomLeftValue(TreeNode root) {
if (root == null) return 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int result = root.val; // 初始化为根节点值
while (!queue.isEmpty()) {
int levelSize = queue.size();
// 每层的第一个节点就是当前层最左边的节点
result = queue.peek().val;
// 处理当前层所有节点
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
}
return result;
}
}
方法二:深度优先遍历
class Solution {
private int maxDepth = -1; // 记录最大深度
private int result = 0; // 记录结果值
/**
* 深度优先遍历:先序遍历,只在首次到达新深度时更新结果
*
* @param root 二叉树根节点
* @return 最底层最左边节点的值
*/
public int findBottomLeftValue(TreeNode root) {
dfs(root, 0);
return result;
}
/**
* DFS函数
*
* @param node 当前节点
* @param depth 当前深度
*/
private void dfs(TreeNode node, int depth) {
if (node == null) return;
// 只有在首次到达新深度时才更新结果(保证是最左边的节点)
if (depth > maxDepth) {
maxDepth = depth;
result = node.val;
}
// 先遍历左子树,再遍历右子树(保证左边优先)
dfs(node.left, depth + 1);
dfs(node.right, depth + 1);
}
}
方法三:右到左层序遍历
import java.util.*;
class Solution {
/**
* 右到左层序遍历:从右到左遍历每层,最后一个节点就是答案
*
* @param root 二叉树根节点
* @return 最底层最左边节点的值
*/
public int findBottomLeftValue(TreeNode root) {
if (root == null) return 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int result = root.val;
while (!queue.isEmpty()) {
int levelSize = queue.size();
// 从右到左遍历当前层
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
result = node.val; // 不断更新,最后一个就是最底层最左边
// 先加入右子节点,再加入左子节点
if (node.right != null) {
queue.offer(node.right);
}
if (node.left != null) {
queue.offer(node.left);
}
}
}
return result;
}
}
方法四:迭代DFS(使用栈)
import java.util.*;
class Solution {
/**
* 迭代DFS:使用栈模拟递归
*
* @param root 二叉树根节点
* @return 最底层最左边节点的值
*/
public int findBottomLeftValue(TreeNode root) {
if (root == null) return 0;
Stack<TreeNode> nodeStack = new Stack<>();
Stack<Integer> depthStack = new Stack<>();
nodeStack.push(root);
depthStack.push(0);
int maxDepth = -1;
int result = 0;
while (!nodeStack.isEmpty()) {
TreeNode node = nodeStack.pop();
int depth = depthStack.pop();
if (depth > maxDepth) {
maxDepth = depth;
result = node.val;
}
// 先压入右子节点,再压入左子节点(栈是后进先出)
if (node.right != null) {
nodeStack.push(node.right);
depthStack.push(depth + 1);
}
if (node.left != null) {
nodeStack.push(node.left);
depthStack.push(depth + 1);
}
}
return result;
}
}
算法分析
- 时间复杂度:O(n) - 所有方法都需要遍历所有节点
- 空间复杂度:
- 层序遍历:O(w) - w是树的最大宽度
- DFS:O(h) - h是树的高度(递归栈深度)
- 右到左层序遍历:O(w)
- 迭代DFS:O(h) - 栈空间
算法过程
输入:root = [1,2,3,4,null,5,6,null,null,7]
树结构:
1
/ \
2 3
/ / \
4 5 6
/
7
DFS:
深度0: 访问1 → maxDepth=0, result=1
深度1: 访问2 → maxDepth=1, result=2
深度2: 访问4 → maxDepth=2, result=4
深度1: 访问3 → depth=1 ≤ maxDepth=2,跳过
深度2: 访问5 → depth=2 ≤ maxDepth=2,跳过
深度3: 访问7 → maxDepth=3, result=7
深度2: 访问6 → depth=2 ≤ maxDepth=3,跳过
最终结果: 7
层序遍历:
层0: [1] → result=1
层1: [2,3] → result=2
层2: [4,5,6] → result=4
层3: [7] → result=7
最终结果: 7
测试用例
public static void main(String[] args) {
Solution solution = new Solution();
// 构建二叉树
TreeNode buildTree(Integer[] arr) {
if (arr.length == 0 || arr[0] == null) return null;
TreeNode root = new TreeNode(arr[0]);
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int i = 1;
while (!queue.isEmpty() && i < arr.length) {
TreeNode node = queue.poll();
if (i < arr.length && arr[i] != null) {
node.left = new TreeNode(arr[i]);
queue.offer(node.left);
}
i++;
if (i < arr.length && arr[i] != null) {
node.right = new TreeNode(arr[i]);
queue.offer(node.right);
}
i++;
}
return root;
}
// 测试用例1:标准示例
TreeNode root1 = buildTree(new Integer[]{2, 1, 3});
System.out.println("Test 1: " + solution.findBottomLeftValue(root1)); // 1
// 测试用例2:复杂树
TreeNode root2 = buildTree(new Integer[]{1, 2, 3, 4, null, 5, 6, null, null, 7});
System.out.println("Test 2: " + solution.findBottomLeftValue(root2)); // 7
// 测试用例3:右偏树
TreeNode root3 = buildTree(new Integer[]{1, null, 2, null, 3});
System.out.println("Test 3: " + solution.findBottomLeftValue(root3)); // 3
// 测试用例4:单节点
TreeNode root4 = new TreeNode(42);
System.out.println("Test 4: " + solution.findBottomLeftValue(root4)); // 42
// 测试用例5:完全二叉树
TreeNode root5 = buildTree(new Integer[]{1, 2, 3, 4, 5, 6, 7});
System.out.println("Test 5: " + solution.findBottomLeftValue(root5)); // 4
// 测试用例6:左偏树
TreeNode root6 = buildTree(new Integer[]{1, 2, null, 3, null, 4});
System.out.println("Test 6: " + solution.findBottomLeftValue(root6)); // 4
// 测试用例7:空树
System.out.println("Test 7: " + solution.findBottomLeftValue(null)); // 0 (根据实现)
// 测试用例8:深度较大的树
TreeNode root8 = buildTree(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
System.out.println("Test 8: " + solution.findBottomLeftValue(root8)); // 8
}
关键点
-
遍历顺序:- DFS需要先遍历左子树,确保左边节点优先被访问
- 层序遍历自然按从左到右的顺序处理每层
-
深度更新:
- 只在首次到达新深度时更新结果,保证是最左边的节点
- 如果在同深度多次更新,会得到最右边的节点
-
边界情况处理:
- 空树:树非空
- 单节点:直接返回根节点值
- 只有右子树:最底层最左边就是最深的右节点
-
空间复杂度:
- 平衡树,DFS空间复杂度O(log n)
- 退化为链表的树,DFS空间复杂度O(n)
- 层序遍历最坏情况空间复杂度O(n)
常见问题
-
为什么DFS要先遍历左子树?
- 需要最左边的节点,先遍历左子树确保在同深度下左边节点先被访问
-
如何找到最底层最右边的节点?
- DFS:先遍历右子树
- 层序遍历:取每层最后一个节点
383

被折叠的 条评论
为什么被折叠?



