当前:LC199 · 二叉树的右视图 · 首次出现于 Day 17 · 路径:顶栏「56天打卡」→ 点击 LC 题号 → 逐题动画

LC199 · Binary Tree Right Side View · 树 / BFS

二叉树右视图:每一层只收下最右边

站在树的右边往左看,同一层会被右边的节点挡住。层序从左到右出队,每层最后一个就是这一层露出来的那个。

用队列做层序遍历,进入一层先记下 size。出队 size 个节点时,最后一个的值写入答案。左孩子先入队、右孩子后入队,因此最后出队的就是本层最右。时间 O(n),空间 O(n)。

时间 O(n)空间 O(n)结论先行 · 全文约 6 节
导读

给你一棵二叉树的根,想象自己站在它的右侧,从上往下看,返回你能看见的节点值。同一层里,更靠右的节点会挡住左边的。空树返回空列表。

主例 root = [1, 2, 3, null, 5, null, 4]。根是 1,左边 2 挂着 5,右边 3 挂着 4。从上往下看见 1、3、4。2 被 3 挡住,5 被 4 挡住。

这不是只走右儿子那一条链:若右子树比左子树矮,更深的左子树节点仍会从右侧露出来。本文要回答:层序怎样取出每层最右,以及主例三层分别看见谁。

层序从左到右,收下每层最后一个

第一直觉是一直走右孩子:1→3→4,主例碰巧对。若 3 没有右孩子、2 下面却还有更深的 5,右链走完就漏掉 5——站在右侧仍能看见那一层唯一的 5。右视图是「每一层最右边的人」,不是「右儿子组成的路径」。

缺的是按层分组。把根放进队列。每一轮先记下当前队列长度 size,只处理这么多个节点,他们属于同一层;处理时把左右孩子接到队尾,留给下一层。从左到右入队、从左到右出队,那么这一轮最后一个出队的,就是本层最靠右的节点,把它写入右视图。

用手走主例。第 0 层队列只有 1,size=1,它既是第一个也是最后一个,view=[1],左右孩子 2、3 入队——演示第一帧。第 1 层 size=2,先出 2、再出 3,最右是 3,view=[1,3];2 把 5 入队,3 把 4 入队——第二帧。第 2 层 size=2,先出 5、再出 4,最右是 4,view=[1,3,4]——第三帧。树走完,答案 [1,3,4]。

size 必须在本轮开始时拍快照。若边出边按「队列空了没」来判断层,新入队的孩子会被算进本层,最右就会错。左孩子先入、右孩子后入,保证同一层从左到右;若先入右孩子,最后出队的反而变成最左,那是左视图。

也可以深度优先:每到一个新深度第一次看见的节点记下来,但访问顺序必须先右后左,这样每层第一次碰到的才是最右。BFS 不必记深度数组,切层更直观。空树队列放不进根,返回空。只有一个节点时,右视图就是它自己。每个节点进出队列一次,时间和空间都随节点数线性增长。

size 切片进入循环先写 size := len(q),再只循环这么多次。新孩子进队尾,不会被本层算进去。主例三轮 size 是 1、2、2,最右分别是 1、3、4。
第 2 层:取最右 4
12354
0
1
右视图
1
第 0 层:[1] → 最右 1

Go:BFS 取每层末尾

solution.goGo
func rightSideView(root *TreeNode) []int {
res := []int{}
if root == nil { return res }
q := []*TreeNode{root}
for len(q) > 0 {
size := len(q)
for i := 0; i < size; i++ {
n := q[0]; q = q[1:]
if i == size-1 { res = append(res, n.Val) }
if n.Left != nil { q = append(q, n.Left) }
if n.Right != nil { q = append(q, n.Right) }
}
}
return res
}

1空树返回空切片。size 固定本层人数,主例三轮是 1、2、2。

2i == size-1 时写入答案。主例三层分别写下 1、3、4。

3左孩子先入队,右孩子后入队,最后出队的才是最右。对调入队顺序会变成左视图。

总结

层序每层取最后一个。主例三层看见 1、3、4。

  • 不是只走右儿子链。右子树较矮时,更深的左子树仍会露出来。
  • 主例第 1 层 [2,3] 取 3,第 2 层 [5,4] 取 4。2 和 5 都被挡住。
  • DFS 先右后左、每层记第一次访问,也能做。切层 BFS 更直观。
同族题目
LC102二叉树的层序遍历LC107二叉树的层序遍历 IILC637二叉树的层平均值