一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情。
一、题目描述
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
示例 1:
输入: [1,2,3,null,5,null,4]
输出: [1,3,4]
示例 2:
输入: [1,null,3]
输出: [1,3]
示例 3:
输入: []
输出: []
提示:
- 二叉树的节点个数的范围是
[0,100] -100 <= Node.val <= 100
二、思路分析
题目要求返回二叉树中每一层的最右边的节点,那么自然地想到一种思路:层序遍历二叉树,在遍历的过程中找到最右边的节点,将其压入答案数组就好了。而二叉树的层序遍历,其实也就是 BFS 。实现过程很简单,代码中有注释。
BFS 可以解题,那么猜想 DFS 是否也能做到。思路如下:递归遍历节点的右子树,如果该节点是当前层的最右的节点,则存进答案数组,不用再看左子树;否则递归遍历节点的左子树,如果是当前层的最右的节点,也可以存进答案数组。但这里有一个难点,就是怎么判断节点是最右的节点。
笔者一开始是想用变量标识的,即当该层的最右节点被找到后,则标识为 true ,那么下一次再遍历到当前层的时候,得知标识为 true ,说明当前层的最右节点已经被找到了,直接遍历下一层。但实践下来,发现行不通,无奈只能换一种方法。
其实判断当前节点是不是最右节点,可以用树的深度加以判断,这里难以描述,直接看注释吧,结合代码跑一下流程,就可以理解了,其实也不难。
三、AC 代码
二叉树构建:
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
BFS:
var rightSideView = function(root) {
if (!root) return [];
let arr = [];
let que = [root];
const bfs = () => {
while (que.length > 0) {
const len = que.length;
let tmp = []; // 存储每一层的节点
// 将 que 数组的节点搬到 tmp 数组中,并将 que 的值更新为下一层的节点
for (let i = 0; i < len; i++) {
const node = que.shift();
tmp.push(node.val);
// 注意:先压左子树,再压右子树
if (node.left) {
que.push(node.left); // 存储下一层的节点
}
if (node.right) {
que.push(node.right);
}
}
// 每一层的节点存储完毕,找到 tmp 数组的末尾元素就是每一层的最右节点
arr.push(tmp[tmp.length - 1]);
// while 循环,继续遍历下一层
}
}
bfs();
return arr;
};
DFS:
var rightSideView = function(root) {
let arr = [];
let depth = 0;
const dfs = (node) => {
if (!node) {
return;
}
depth++;
if (arr.length < depth) {
// 这一层还没有记录值
// 说明 root 就是右侧视图的第一个节点
arr.push(node.val);
}
// 注意:先递归右子树
dfs(node.right);
dfs(node.left);
// 到底了,底下没有其他节点了,开始回溯
depth--;
}
dfs(root);
return arr;
};