持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情
题目(Binary Tree Right Side View)
链接:https://leetcode-cn.com/problems/binary-tree-right-side-view
解决数:2224
通过率:65.6%
标签:树 深度优先搜索 广度优先搜索 二叉树
相关公司:facebook bytedance amazon
给定一个二叉树的 根节点 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
思路
给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
翻译过来,就是:取每层节点中最右侧节点的值。
关键点在于“最右侧”,看两个例子:
例一:二叉树[1, 2, 3, 4]的期望答案是[1, 3, 4]。
显然:
- 顶层(depth为0的一层)只有一个元素,val为1
- 次成(depth为1的一层)有两个元素,节点2(左节点)和节点3(右节点),所以该层的val应该为3
- 底层(depth为2的一层)只有一个元素,即节点4(左节点),所以该层的val应该为4
例二:二叉树[1, 2, 3, null,4]的期望答案是[1, 3, 4]。
显然:
- 顶层(depth为0的一层)只有一个元素,val为1
- 次成(depth为1的一层)有两个元素,节点2(左节点)和节点3(右节点),所以该层的val应该为3
- 底层(depth为2的一层)有两个元素,即空(左节点)和节点4(右节点),所以该层的val应该为4
虽然两棵树的答案都是[1, 3, 4]。但得到这个结果的原因是不一样的。
二叉树[1, 2, 3, 4]在depth为2的那层,没有右节点,所以该层的值是左节点的val。可以将该二叉树理解为[1, 2, 3, 4, null]。
而二叉树[1, 2, 3, null, 4]在depth为2的那层,存在右节点,所以该层的左节点无论是不是null,该层都应取右节点的val。可以将该二叉树理解为[1, 2, 3, ?, 4]。
逻辑梳理
明白了题意之后,逻辑就易于分析了。
层级遍历
很显然,常见树的层次遍历有DFS和BFS两种方法。
最右侧节点的择取
由于BFS的本质是队列,所以可以保证,在每一次的遍历中,最右边的节点一定是该层的最后一个被遍历到的,所以本题采用BFS来解题,会容易理解一些。
答案数组的实现
首先,我们需要知道是哪一层,其次,无论该层的节点有多少,我们最终的都只是存取最右边的节点。
满足这种形式的数据结构,非Map莫属了。
我们可以使用key来存放层次,用value来存放节点的val。
并且因为使用的是BFS,所以可以保证每层的最右边节点一定是该层的最后一次读取,那么就意味着:该节点的val,一定会更新(覆盖)该层其他节点的val。
不过,题意要求返回的是数组(Array),而现在我们使用的是Map,所以我们需在返回时进行进一步的变更。
答案数组存放的是按层次的val,最简单的方法就是遍历Map(map的遍历顺序由其key的添加顺序决定)取每个key的value。
在ES6中,已经有原生的API来实现该需求了,那就是Map.prototype.values(),想详细了解该API的同学可以查看MDN上的这篇文档。
需要注意的是,该API返回的是一个可迭代对象(Iterator),所以需要再次利用ES6中的展开语法(...)将其构造成数组,从而返回期待答案。
代码
以下是具体的代码实现,结合注释食用更佳~
var rightSideView = function(root) {
if(!root) return [];
// Map存储,key是当前节点的高度,value是当前节点的值
let depthMap = new Map();
// 构造队列,并赋予队首元素的初始高度
let queue = [[root, 0]];
while(queue.length) {
// 取出队首元素
let [ {val, left, right}, depth ] = queue.shift();
/*
关键点
更新Map中每个key所对应的val,
因为是BFS,所以可以保证最终Map的key所对应的val是同一层节点中的最右边节点的val
*/
depthMap.set(depth, val);
// 高度递增
depth += 1;
// 仅将存在的节点推入队列中
if(left) {
queue.push([left, depth]);
}
// 仅将存在的节点推入队列中
if(right) {
queue.push([right, depth]);
}
}
// Map.prototype.values()返回是可迭代对象,故需利用“展开语法”来将其转换为数组
return [...depthMap.values()];
}