「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」。
题目
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例1
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
示例2
输入:root = [1]
输出:[[1]]
示例3
输入:root = []
输出:[]
思路
本题是个二叉树的数据结构,因为二叉树的每个节点,基本都有相同的结构,所以递归的解法是不错的选择。
从例1的结果来看,若 root
具有 n
层,则答案 ans
应具有 n
个元素,且每个元素是数组类型,每个元素里依次存放着按顺序访问的节点值。但是递归的顺序一般都为 DFS(深度优先搜索)
,即意味着没法按照层的顺序从左到右访问每个节点。
既然递归没法按照顺序访问节点,那可以为每个节点设置一个辅助值 level
,代表该节点的层级;然后对于每个节点,按照先访问左子树,再访问右子树的顺序,访问整棵树。根节点,可以视为 第0层
;
递归完整代码如下
/**
* 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[][]}
*/
var levelOrder = function(root) {
const ans = [];
const dfs = (root, level) => {
// 节点为空,结束递归
if(!root) return;
// 初始化元素为 []
if(!ans[level]) {
ans[level] = [];
}
// 按照层级,放入对应的元素中
ans[level].push(root.val);
// 将 level + 1,依次访问左、右子树
dfs(root.left, level + 1);
dfs(root.right, level + 1);
}
// 访问根节点
dfs(root, 0);
return ans;
};
思考: 有没有一种方式,可以按照层级,从左到右依次遍历整棵树呢?
有一种好的方法是,维护一个栈 stack
, 先将某个层级的节点入栈,当这个层级入栈都完成后,当前栈里的元素个数,就是当前层级的元素个数;这时,将这些元素依次出栈,出栈时,保存当前节点的数值,同时将节点的左右子树放入栈里。当栈不为空时,就继续这个过程。当栈为空,树也就遍历完成了,且也实现了按照层级顺序,从左到右依次访问节点;
广度优先遍历完整代码
/**
* @param {TreeNode} root
* @return {number[][]}
*/
var levelOrder = function(root) {
const ans = [];
if(!root) {
return ans;
}
const stack = [];
// 根节点入栈
stack.push(root);
while(stack.length != 0) {
// 保存当前层级的节点个数
const levelSize = stack.length;
// 先放入初始化的数据结构
ans.push([]);
for(let i = 0; i < levelSize; i++) {
// 栈头出栈
const node = stack.shift();
// 访问元素值并保存
ans[ans.length - 1].push(node.val);
// 左右节点入栈
if(node.left) stack.push(node.left);
if(node.right) stack.push(node.right);
}
}
return ans;
};
小结
深度优先遍历的算法,一般会用到递归;广度优化的遍历,则一般需要维护一个额外的栈,用于保存当前层级的节点。两种遍历方式的时间复杂度一般都为 O(n)
,即每个节点都会被访问一次。对于二叉树的遍历,两种遍历方式都需要掌握才行。
LeetCode 👉 HOT 100 👉 二叉树的层序遍历 - 中等题 ✅
合集:LeetCode 👉 HOT 100,有空就会更新,大家多多支持,点个赞👍
如果大家有好的解法,或者发现本文理解不对的地方,欢迎留言评论 😄