二叉树层序遍历 - 专题
从左到右遍历。一般借助队列结构,待输出的节点从队尾入队,队头出队。
- 队列队首元素为下一个待出队的元素
- 每次将队首节点出队输出当前节点(一般为
val字段)后; - 查询该节点是否存在左右子树节点,如果存在则将其从队尾入队
- 循环上述步骤;直到队为空
示例
现有一个二叉树总共有5个节点。 层序遍历思路如下:
- 蓝色节点表示已经入队
- 橙色表示未入队
经典例题
题目 01 - 二叉树层序遍历
原题链接
题目描述
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)
思路
- 依照图示;使用队列辅助实现
代码
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> level;
//检查输入
if(null == root) return ans;
//辅助队列; 先加入头节点
Deque<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()) {
//获取当前层拥有几个节点
int size = queue.size();
//记录当前层的所有元素
level = new ArrayList<>();
//遍历当前层内的所有结点
for(int i = 0; i < size; i++) {
TreeNode node = queue.pop();
level.add(node.val);
//记录下一层的左子节点
if(node.left != null) queue.offer(node.left);
if(node.right != null) queue.offer(node.right);
}
//记录当前层记录到的所有结果结点
ans.add(level);
}
return ans;
}
}
结果分析
- 时间复杂度:
O(N) - 空间复杂度:
O(N)
题目 02 - 二叉树层序遍历Ⅱ
原题链接
题目描述
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)
思路
- 同第一题思路一致;最后把记录结果的集合反转
代码
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
//记录每一层遍历的结果
List<List<Integer>> ans = new LinkedList<>();
//检查输入
if(root == null) return ans;
//层序遍历
Deque<TreeNode> queue = new LinkedList<>();
//先记录根结点结果
queue.offer(root);
while(!queue.isEmpty()) {
int size = queue.size();
//遍历上一层记录的所有结点
List<Integer> level = new ArrayList<>();
for(int i = 0; i < size; i++) {
TreeNode tmp = queue.pop();
//记录该层,顺序正常
level.add(tmp.val);
//如果左右子节点不为空,则将其加入到下一层遍历
if(tmp.left != null) queue.add(tmp.left);
if(tmp.right != null) queue.add(tmp.right);
}
//方法一: 添加到首部;使得输出结果翻转
ans.addFirst(level);
/*
方法二: 或者基于 Collections 工具集
ans.add(level);
*/
}
/*
方法二: 或者基于 Collections 工具集
Collections.reverse(ans);
*/
return ans;
}
}
结果分析
- 时间复杂度:
O(N) - 空间复杂度:
O(N)
题目 03 - 二叉树的右视图
原题链接
题目描述
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
思路
- 判断是否遍历到单层最后面的元素,如果是,就放进结果集中;
代码
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> ans = new LinkedList<>();
if(root == null) return ans;
//遍历每一层的结果
Deque<TreeNode> level = new LinkedList<>();
//将每一层的最后一个结点加入结果集
level.offer(root);
while(!level.isEmpty()) {
int size = level.size();
for(int i = 0; i < size; i++) {
TreeNode tmp = level.pop();
if(i == size - 1) {
ans.add(tmp.val);
}
//如果左右结点不为空;加入结果集内
if(tmp.left != null) level.add(tmp.left);
if(tmp.right != null) level.add(tmp.right);
}
}
return ans;
}
}
结果分析
- 时间复杂度:
O(N) - 空间复杂度:
O(N)
题目 04 - 求二叉树的平均值
原题链接
题目描述
给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。
思路
- 层序遍历,每一层求总和之后与当前层内二叉树结点个数相除
代码
class Solution {
/*
* 层序遍历,每一层求总和之后与当前层内二叉树结点个数相除
*/
public List<Double> averageOfLevels(TreeNode root) {
List<Double> ans = new LinkedList<>();
if (root == null)
return ans;
Deque<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) { // 队列不为空出队获取队首节点
int size = queue.size();
Double sum = 0.0;
for (int i = 0; i < size; i++) {
TreeNode peek = queue.poll();
sum += peek.val;
if (peek.left != null) { // 左子节点不为空
queue.offer(peek.left);
}
if (peek.right != null) { // 右子节点不为空
queue.offer(peek.right);
}
}
ans.add(sum / size); // 计算该层的节点值平均值
}
return ans;
}
}
结果分析
- 时间复杂度:
O(N) - 空间复杂度:
O(N)
题目 05 - N 叉树的层序遍历
原题链接
题目描述
给定一个 N 叉树,返回其节点值的层序遍历 (即从左到右,逐层遍历)。 树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。
思路
- 多了一个子节点,但是代码主体逻辑不变
- 由于是 N 叉树,子节点使用 List 集合存储
- 将判断是否还有子节点的逻辑切换为判断
List集合是否为空
代码
class Solution {
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> ans = new LinkedList<>();
if (root == null) { // 检查输入
return ans;
}
Deque<Node> queue = new LinkedList<>(); // 队列,用于层序遍历
queue.offer(root); // 根节点入队
while (!queue.isEmpty()) {
int size = queue.size(); // 本层节点的个数
List<Integer> level = new LinkedList<>(); // 本层遍历的节点结果
for (int i = 0; i < size; i++) {
Node peek = queue.poll();
level.add(peek.val);
if (!peek.children.isEmpty()) {
// 如果当前节点的子节点不为空
for (int j = 0; j < peek.children.size(); j++) {
// 将其子孩子加入到队列
queue.offer(peek.children.get(j));
}
}
}
// 将层的遍历结果加入结果集 ans
ans.add(level);
}
return ans;
}
}
结果分析
- 时间复杂度:
O(N) - 空间复杂度:
O(N)
题目 06 - 在每个树行中找到最大值
原题链接
题目描述
给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。
思路
- 同样,借助队列对每一层完成遍历
- 使用一个临时变量
max记录每一层的最大值; - 每遍历完一层将
max加入到结果集内
代码
class Solution {
public List<Integer> largestValues(TreeNode root) {
List<Integer> ans = new LinkedList<>();
if (root == null) { // 检查输入
return ans;
}
// 根节点入队
Deque<TreeNode> queue = new LinkedList<>();
queue.offer(root);
// 遍历每一层获取到最大值
while (!queue.isEmpty()) {
// 得到这一层节点的个数
int size = queue.size();
int max = Integer.MIN_VALUE; // 遍历记录当前层的最大值
for (int i = 0; i < size; i++) {
TreeNode peek = queue.poll();
max = Math.max(max, peek.val); // 更新当前层最大值
if (peek.left != null) { // 如果左右节点不为空,将其入队
queue.offer(peek.left);
}
if (peek.right != null) {
queue.offer(peek.right);
}
}
ans.add(max); // 将最大值记录
}
return ans;
}
}
结果分析
- 时间复杂度:
O(N) - 空间复杂度:
O(N)
题目 07 - 填充每个节点的下一个右侧节点指针
原题链接
LeetCode 116 - 填充每个节点的下一个右侧节点指针
题目描述
给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。 如果找不到下一个右侧节点,则将 next 指针设置为 NULL。 初始状态下,所有 next 指针都被设置为 NULL。
思路
- 同样,借助队列对每一层完成遍历
- 每一层借助临时变量
prev记录上一个遍历到的节点 - 如果
prev == null遍历到该层的首个节点, - 当
prev不为null, 则当前遍历到的不是首个节点
代码
class Solution {
public Node connect(Node root) {
Deque<Node> queue = new LinkedList<>();
// 检查输入
if (root == null) {
return root;
}
// 将节点加入到队列
queue.offer(root);
// 遍历每一层
while (!queue.isEmpty()) {
// 当前树层的节点数目
int size = queue.size();
Node pre = null;
// 遍历当前层所有节点并记录其子节点
for (int i = 0; i < size; i++) {
Node tmp = queue.pop();
// 如果记录前驱节点不为空,则更新 pre 的 next 域指向 tmp
if (pre != null) {
pre.next = tmp;
}
// 更新前驱结点
pre = tmp;
// 如果存在左右结点,将其入队
if (pre.left != null) {
queue.offer(tmp.left);
}
if (pre.right != null) {
queue.offer(tmp.right);
}
}
}
// 返回根节点
return root;
}
}
结果分析
- 时间复杂度:
O(N) - 空间复杂度:
O(N)
题目 08 - 二叉树的最大深度
原题链接
题目描述
给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
思路
- 借助队列,遍历每一层内的各个节点;
depth用于记录深度 - 如果当前节点存在子节点,说明还有下一层;深度 + 1
- 如果遍历完当前层内的各个节点均不存在子节点;即得到最大深度
代码
class Solution {
public int maxDepth(TreeNode root) {
int depth = 0; // 记录最大深度
if (root == null) { // 检查输入
return 0;
}
Deque<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
depth++; // 更新深度
for (int i = 0; i < size; i++) {
TreeNode peek = queue.pop();
// 左子节点不为空则入队
if (peek.left != null) {
queue.offer(peek.left);
}
// 右子节点不为空则入队
if (peek.right != null) {
queue.offer(peek.right);
}
}
}
return depth;
}
}
结果分析
- 时间复杂度:
O(N) - 空间复杂度:
O(N)
题目 09 - 二叉树的最小深度
原题链接
题目描述
给定一个二叉树,找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明:叶子节点是指没有子节点的节点。
思路
- 层序遍历,若当前树层存在节点且该节点左或者右节点存在,更新最小深度
- 如果遍历该节点左右节点都为空,则返回最小深度
代码
class Solution {
public int minDepth(TreeNode root) {
if (root == null) { // 检查输入
return 0;
}
Deque<TreeNode> queue = new LinkedList<>();
int minDepth = 1; // 仅有根节点的情况;深度为 1
queue.offer(root); // 根节点入队
if (root.left == null && root.right == null) {
return minDepth;
}
// 判断树下一层是否存在节点
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode peek = queue.pop(); // 队首节点
if (peek.left == null && peek.right == null) {
return minDepth;
}
if (peek.left != null) {
queue.offer(peek.left);
}
if (peek.right != null) {
queue.offer(peek.right);
}
}
minDepth++; // 更新最小深度
}
// 否则无最小深度
throw new RuntimeException("Not such element!");
}
}
结果分析
- 时间复杂度:
O(N) - 空间复杂度:
O(N)