算法题 找树左下角的值

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

算法思路

关键

  1. 最底层:深度最大的层
  2. 最左边:在最底层中,从左到右第一个节点

方法

  1. 层序遍历(BFS):按层遍历,最后一层的第一个节点就是答案
  2. 深度优先遍历(DFS):记录最大深度和对应的左节点值
  3. 右到左层序遍历:从右到左遍历每层,最后一个访问的节点就是答案

代码实现

方法一:层序遍历

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
}

关键点

  1. 遍历顺序

    • DFS需要先遍历左子树,确保左边节点优先被访问
    • 层序遍历自然按从左到右的顺序处理每层
  2. 深度更新

    • 只在首次到达新深度时更新结果,保证是最左边的节点
    • 如果在同深度多次更新,会得到最右边的节点
  3. 边界情况处理

    • 空树:树非空
    • 单节点:直接返回根节点值
    • 只有右子树:最底层最左边就是最深的右节点
  4. 空间复杂度

    • 平衡树,DFS空间复杂度O(log n)
    • 退化为链表的树,DFS空间复杂度O(n)
    • 层序遍历最坏情况空间复杂度O(n)

常见问题

  1. 为什么DFS要先遍历左子树?

    • 需要最左边的节点,先遍历左子树确保在同深度下左边节点先被访问
  2. 如何找到最底层最右边的节点?

    • DFS:先遍历右子树
    • 层序遍历:取每层最后一个节点
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值