力扣解题-199. 二叉树的右视图

0 阅读7分钟

力扣解题-199. 二叉树的右视图

给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

示例 1:

image.png

输入:root = [1,2,3,null,5,null,4]

输出:[1,3,4]

示例 2:

image.png

输入:root = [1,2,3,4,null,null,null,5]

输出:[1,3,4,5]

示例 3:

输入:root = [1,null,3]

输出:[1,3]

示例 4:

输入:root = []

输出:[]

提示:

二叉树的节点个数的范围是 [0,100]

-100 <= Node.val <= 100

Related Topics

树、深度优先搜索、广度优先搜索、二叉树


第一次解答

解题思路

核心方法:广度优先搜索(BFS)层级遍历优化法,基于层序遍历的核心逻辑,优先遍历每一层的右子节点,且仅记录每一层的第一个节点值(即右侧能看到的节点),时间复杂度O(n)、空间复杂度O(n),是本题直观且高效的解法。

核心逻辑拆解

二叉树右视图的核心是“获取每一层最右侧的节点值”,关键在于调整层序遍历的节点入队顺序并精准筛选目标节点:

  1. 空树处理:若根节点root == null,直接返回空列表(无任何节点可见);
  2. 初始化:创建结果列表res存储右视图节点值,队列queue存储待遍历节点,先将根节点入队;
  3. 层级遍历循环:队列非空时,持续处理每一层:
    • 记录层大小:遍历前获取队列长度size(即当前层的节点数);
    • 遍历当前层节点:通过倒序循环(i>0)处理当前层所有节点,核心逻辑:
      • i == size(即当前是该层第一个处理的节点),将节点值加入结果列表(这是该层最右侧节点);
      • 调整入队顺序:先将节点的右子节点入队,再入队左子节点(保证下一层遍历从右到左,第一个处理的就是最右侧节点);
  4. 返回结果:遍历完所有层后,返回存储右视图节点值的列表。
具体步骤(以示例1 root=[1,2,3,null,5,null,4]为例)
遍历层级队列初始状态层大小size关键操作(i=size时记录值)队列最终状态结果列表更新
0(根层)[1]1i=1 → 记录1[3,2][1]
1[3,2]2i=2 → 记录3[4,5][1,3]
2[4,5]2i=2 → 记录4[][1,3,4]
关键细节说明
  • 入队顺序优化:先右后左的入队顺序,确保每一层第一个被处理的节点就是最右侧节点,无需遍历完该层所有节点再找最后一个;
  • 筛选条件i == size 是核心筛选逻辑,仅记录每一层第一个处理的节点值,避免冗余存储;
  • 边界处理:单独处理空树场景,符合题目中“空树返回空列表”的要求;
  • 鲁棒性:即使某层只有左节点(无右节点),也能正确记录该节点为右视图节点(如某层仅节点2,会被正常记录)。
性能说明
  • 时间复杂度:O(n)(每个节点仅入队/出队一次,n为节点总数);
  • 空间复杂度:O(n)(最坏情况队列存储一层所有节点,如完全二叉树最后一层有n/2个节点);
  • 优势:
    1. 基于经典层序遍历改造,逻辑直观,易理解和维护;
    2. 无需遍历完层所有节点再筛选,提前记录目标值,效率无冗余;
    3. 代码简洁,仅需调整入队顺序和筛选条件即可实现需求。
    public List<Integer> rightSideView(TreeNode root) {
        if(root==null) {
            return new ArrayList<>();
        }
        List<Integer> res=new ArrayList<>();
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            //都是取第一个做为res里面的值
            int size=queue.size();
            for(int i=size;i>0;i--){
                TreeNode node=queue.poll();
                if(i==size) {
                    res.add(node.val);
                }
                if(node.right!=null){
                    queue.offer(node.right);
                }
                if(node.left!=null){
                    queue.offer(node.left);
                }
            }
        }
        return res;
    }

示例解答

解题思路

解法1:传统BFS层序遍历法(取每层最后一个节点)

核心方法:沿用标准层序遍历逻辑(先左后右入队),遍历完每一层所有节点后,记录该层最后一个节点值,逻辑更通用,无需调整入队顺序。

代码实现
public List<Integer> rightSideView(TreeNode root) {
    if (root == null) {
        return new ArrayList<>();
    }
    List<Integer> res = new ArrayList<>();
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);
    
    while (!queue.isEmpty()) {
        int size = queue.size();
        TreeNode lastNode = null;
        // 遍历当前层所有节点,记录最后一个节点
        for (int i = 0; i < size; i++) {
            lastNode = queue.poll();
            // 先左后右入队(标准层序遍历)
            if (lastNode.left != null) {
                queue.offer(lastNode.left);
            }
            if (lastNode.right != null) {
                queue.offer(lastNode.right);
            }
        }
        // 记录当前层最后一个节点值(右侧可见)
        res.add(lastNode.val);
    }
    return res;
}
核心逻辑说明
  1. 标准层序遍历:保持先左后右的节点入队顺序,符合常规层序遍历习惯;
  2. 记录最后节点:遍历层内所有节点时,用lastNode记录最后一个弹出的节点(即该层最右侧节点);
  3. 存储结果:每层遍历结束后,将lastNode.val加入结果列表。
性能说明
  • 时间复杂度:O(n)(与原解法一致);
  • 空间复杂度:O(n)(与原解法一致);
  • 优势:
    1. 逻辑更通用,无需调整入队顺序,新手易理解;
    2. 可无缝拓展为“左视图”(记录每层第一个节点);
  • 劣势:需遍历完层内所有节点才能确定目标值,无提前终止的优化,但整体效率无本质差异。
解法2:DFS递归法(优先遍历右子树)

核心方法:深度优先搜索,优先遍历右子树,首次访问某一层级时记录节点值(即为该层最右侧节点),时间复杂度O(n)、空间复杂度O(h)(h为树的高度)。

代码实现
public List<Integer> rightSideView(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    // 从根节点、层级0开始递归,优先遍历右子树
    dfs(root, 0, res);
    return res;
}

private void dfs(TreeNode node, int level, List<Integer> res) {
    if (node == null) {
        return;
    }
    // 首次访问该层级,记录节点值(优先右子树,故为最右侧节点)
    if (level == res.size()) {
        res.add(node.val);
    }
    // 优先递归右子树,再递归左子树
    dfs(node.right, level + 1, res);
    dfs(node.left, level + 1, res);
}
核心逻辑说明
  1. 递归参数level表示当前节点的层级(根节点为0),res存储右视图节点值;
  2. 层级判断:当level == res.size()时,说明是首次访问该层级,此时的节点是该层最右侧节点(因优先遍历右子树);
  3. 遍历顺序:先递归右子树,再递归左子树,确保首次访问某层级的节点是最右侧节点。
性能说明
  • 时间复杂度:O(n)(每个节点仅被访问一次);
  • 空间复杂度:O(h)(递归栈深度等于树的高度,平衡树为O(logn),斜树为O(n));
  • 优势:
    1. 无需使用队列,空间复杂度在平衡树场景下优于BFS;
    2. 代码更简洁,递归逻辑贴合“找最右侧节点”的语义;
  • 劣势:
    1. 逻辑稍抽象,依赖递归栈,极端斜树场景可能导致栈溢出;
    2. 无法提前终止遍历,需访问所有节点。

总结

  1. BFS优先右子节点法(第一次解答):O(n)时间+O(n)空间,提前记录目标值,效率无冗余,是本题的工程优选解法;
  2. 标准BFS层序遍历法:O(n)时间+O(n)空间,逻辑通用易理解,适合新手入门;
  3. DFS递归法:O(n)时间+O(h)空间,无需队列,平衡树场景下空间更优;
  4. 关键技巧
    • 核心思想:右视图的本质是“获取每一层最右侧节点值”,BFS通过入队顺序/层末节点筛选,DFS通过优先遍历右子树+层级标记实现;
    • 顺序优化:BFS中先右后左入队可提前记录目标值,减少冗余判断;
    • 边界处理:必须单独处理空树场景,返回空列表符合题目要求。