面试经典算法系列之二叉树3 -- 二叉树的层序遍历

面试经典算法18 - 二叉树的层序遍历

LeetCode.102
公众号:阿Q技术站

问题描述

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

示例 2:

输入:root = [1]
输出:[[1]]

示例 3:

输入:root = []
输出:[]

提示:

  • 树中节点数目在范围 [0, 2000]
  • -1000 <= Node.val <= 1000

思路

递归
  1. 定义一个辅助函数 levelOrderHelper,用来递归遍历二叉树的每一层,并将节点值按层级存储在 result 中。
  2. 辅助函数的参数包括当前节点 root、当前层级 level 和存储结果的二维数组 result
  3. 如果当前节点为空,则直接返回。
  4. 如果当前层级大于等于 result 的大小,说明需要添加新的一层,因为层序遍历是逐层遍历的。
  5. 将当前节点的值加入到 result 的对应层级中。
  6. 递归遍历当前节点的左子树,层级加一。
  7. 递归遍历当前节点的右子树,层级加一。
  8. 主函数 levelOrder 中调用辅助函数 levelOrderHelper 开始遍历整棵树。
  9. 最后返回存储结果的二维数组 result
非递归
  1. 创建一个队列 queue 和一个存储结果的二维数组 result
  2. 将根节点入队。
  3. 循环直到队列为空:
    • 从队列中取出一个节点,将其值存储到当前层级的结果数组中。
    • 如果节点有左子节点,则将左子节点入队。
    • 如果节点有右子节点,则将右子节点入队。
  4. 将当前层级的结果数组加入到最终的结果中。
  5. 继续循环直到队列为空,完成整个二叉树的层序遍历。

图解

  1. 创建一个队列 queue 和一个存储结果的二维数组 result,将根节点入队。

  1. 队列不为空时,新建一个空数组level,用于存储当前层级节点值,然后进行循环打印,循环次数为当前层的节点数,也就是队列的长度。

  1. 将队头元素弹出,存入当前层级的数组中。

  1. 接着将当前节点的左右节点依次入队(如果有),然后将当前层级的节点值数组加入结果中。

  1. 继续第3步。

  1. 继续第4步。

  1. 继续第3步。

  1. 继续第4步。

此时,队列为空,退出循环,返回结果集。

参考代码

C++
递归
#include <iostream>
#include <vector>using namespace std;// 二叉树节点的定义
struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};// 辅助函数,递归实现层序遍历
void levelOrderHelper(TreeNode* root, int level, vector<vector<int>>& result) {if (!root) return;if (level >= result.size()) {result.push_back({});}result[level].push_back(root->val);levelOrderHelper(root->left, level + 1, result);levelOrderHelper(root->right, level + 1, result);
}// 主函数,返回二叉树的层序遍历结果
vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> result;levelOrderHelper(root, 0, result);return result;
}// 创建二叉树
TreeNode* createTree(vector<int>& nodes, int index) {if (index >= nodes.size() || nodes[index] == -1) {return nullptr; // 如果节点为空,则返回nullptr}TreeNode* root = new TreeNode(nodes[index]); // 创建当前节点root->left = createTree(nodes, 2 * index + 1); // 创建左子树root->right = createTree(nodes, 2 * index + 2); // 创建右子树return root; // 返回当前节点
}// 销毁二叉树
void destroyTree(TreeNode* root) {if (!root) return; // 如果根节点为空,直接返回destroyTree(root->left); // 递归销毁左子树destroyTree(root->right); // 递归销毁右子树delete root; // 删除当前节点
}int main() {// 输入数据,示例二叉树为 {3,9,20,#,#,15,7}vector<int> nodes = {3, 9, 20, -1, -1, 15, 7};TreeNode* root = createTree(nodes, 0); // 创建二叉树// 调用层序遍历函数vector<vector<int>> result = levelOrder(root);// 输出遍历结果for (vector<int> level : result) {for (int val : level) {cout << val << " ";}cout << endl;}// 销毁二叉树destroyTree(root);return 0;
}
非递归
#include <iostream>
#include <vector>
#include <queue>using namespace std;// 二叉树节点的定义
struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> result; // 存储结果的二维数组if (!root) return result; // 如果根节点为空,直接返回空结果数组queue<TreeNode*> q; // 辅助队列,用于层序遍历q.push(root); // 将根节点入队while (!q.empty()) {int size = q.size(); // 当前层级的节点数vector<int> level; // 存储当前层级节点值的数组for (int i = 0; i < size; i++) {TreeNode* node = q.front(); // 获取队头节点q.pop(); // 弹出队头节点level.push_back(node->val); // 将节点值加入当前层级的数组中if (node->left) q.push(node->left); // 将左子节点入队if (node->right) q.push(node->right); // 将右子节点入队}result.push_back(level); // 将当前层级的节点值数组加入结果中}return result; // 返回层序遍历的结果数组
}// 创建二叉树
TreeNode* createTree(vector<int>& nodes, int index) {if (index >= nodes.size() || nodes[index] == -1) {return nullptr; // 如果节点为空,则返回nullptr}TreeNode* root = new TreeNode(nodes[index]); // 创建当前节点root->left = createTree(nodes, 2 * index + 1); // 创建左子树root->right = createTree(nodes, 2 * index + 2); // 创建右子树return root; // 返回当前节点
}// 销毁二叉树
void destroyTree(TreeNode* root) {if (!root) return; // 如果根节点为空,直接返回destroyTree(root->left); // 递归销毁左子树destroyTree(root->right); // 递归销毁右子树delete root; // 删除当前节点
}int main() {vector<int> nodes = {3, 9, 20, -1, -1, 15, 7}; // 二叉树的层序遍历序列TreeNode* root = createTree(nodes, 0); // 创建二叉树vector<vector<int>> result = levelOrder(root); // 进行层序遍历for (auto& level : result) { // 输出遍历结果for (int val : level) {cout << val << " ";}cout << endl;}destroyTree(root); // 销毁二叉树return 0;
}
Java
import java.util.*;// 二叉树节点的定义
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) { val = x; }
}public class Main {public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> result = new ArrayList<>(); // 存储结果的二维数组if (root == null) return result; // 如果根节点为空,直接返回空结果数组Queue<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 node = queue.poll(); // 获取队头节点level.add(node.val); // 将节点值加入当前层级的数组中if (node.left != null) queue.offer(node.left); // 将左子节点入队if (node.right != null) queue.offer(node.right); // 将右子节点入队}result.add(level); // 将当前层级的节点值数组加入结果中}return result; // 返回层序遍历的结果数组}// 创建二叉树public TreeNode createTree(Integer[] nodes, int index) {if (index >= nodes.length || nodes[index] == null) {return null; // 如果节点为空,则返回null}TreeNode root = new TreeNode(nodes[index]); // 创建当前节点root.left = createTree(nodes, 2 * index + 1); // 创建左子树root.right = createTree(nodes, 2 * index + 2); // 创建右子树return root; // 返回当前节点}// 销毁二叉树public void destroyTree(TreeNode root) {if (root == null) return; // 如果根节点为空,直接返回destroyTree(root.left); // 递归销毁左子树destroyTree(root.right); // 递归销毁右子树root = null; // 将当前节点置为null}public static void main(String[] args) {Integer[] nodes = {3, 9, 20, null, null, 15, 7}; // 二叉树的层序遍历序列Main main = new Main();TreeNode root = main.createTree(nodes, 0); // 创建二叉树List<List<Integer>> result = main.levelOrder(root); // 进行层序遍历for (List<Integer> level : result) { // 输出遍历结果for (int val : level) {System.out.print(val + " ");}System.out.println();}main.destroyTree(root); // 销毁二叉树}
}
Python
from typing import List
from collections import deque# 二叉树节点的定义
class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = rightdef levelOrder(root: TreeNode) -> List[List[int]]:result = [] # 存储结果的二维数组if not root:return result # 如果根节点为空,直接返回空结果数组queue = deque([root]) # 辅助队列,用于层序遍历while queue:level_size = len(queue) # 当前层级的节点数level = [] # 存储当前层级节点值的数组for _ in range(level_size):node = queue.popleft() # 获取队头节点level.append(node.val) # 将节点值加入当前层级的数组中if node.left:queue.append(node.left) # 将左子节点入队if node.right:queue.append(node.right) # 将右子节点入队result.append(level) # 将当前层级的节点值数组加入结果中return result # 返回层序遍历的结果数组# 创建二叉树
def createTree(nodes: List[int], index: int) -> TreeNode:if index >= len(nodes) or nodes[index] is None:return None # 如果节点为空,则返回Noneroot = TreeNode(nodes[index]) # 创建当前节点root.left = createTree(nodes, 2 * index + 1) # 创建左子树root.right = createTree(nodes, 2 * index + 2) # 创建右子树return root # 返回当前节点# 销毁二叉树
def destroyTree(root: TreeNode) -> None:if not root:return # 如果根节点为空,直接返回destroyTree(root.left) # 递归销毁左子树destroyTree(root.right) # 递归销毁右子树root = None # 将当前节点置为None# 测试代码
nodes = [3, 9, 20, None, None, 15, 7] # 二叉树的层序遍历序列
root = createTree(nodes, 0) # 创建二叉树
result = levelOrder(root) # 进行层序遍历
for level in result: # 输出遍历结果print(level)destroyTree(root) # 销毁二叉树

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/611105.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python学习之-matplotlib详解

前言&#xff1a; Matplotlib 是一个 Python 的图表绘制库&#xff0c;广泛用于生成各种静态、动态和交互式的图表。它能够创建线图、散点图、条形图、饼图、直方图、误差线图、箱型图、热图、子图网络、散点矩阵等图表。 安装 Matplotlib&#xff1a; pip install matplotli…

KKVIEW远程远程访问家里电脑

远程访问家里电脑&#xff1a;简易指南与价值所在 在数字化时代&#xff0c;电脑已成为我们日常生活和工作中不可或缺的工具。有时&#xff0c;我们可能在外出时急需访问家中电脑里的某个文件或应用&#xff0c;这时&#xff0c;远程访问家里电脑就显得尤为重要。本文将简要介…

计算机网络——交换机和路由器

目录 前言 引言 交换机是用来做什么的&#xff1f; 与路由器有什么区别&#xff1f; 网关 子网掩码 网关、路由 前言 本博客是博主用于复习计算机网络的博客&#xff0c;如果疏忽出现错误&#xff0c;还望各位指正。 这篇博客是在B站掌芝士zzs这个UP主的视频的总结&am…

Ubuntu下配置Android NDK环境

Android-NDK的下载 下载Android-NDK wget -c http://dl.google.com/android/ndk/android-ndk-r10e-linux-x86_64.bin 执行bin文件&#xff08;即解压&#xff09; ./android-ndk-r10c-linux-x86_64.bin Android-NDK的配置 要想使用Android-NDK&#xff0c;还需要进行环境变量…

C/C++基础----判断和循环

判断 if-elseif-else判断 语句&#xff1a; 条件使用之前的逻辑运算符或者关系运算符 if(条件1){条件1成立时内容 }else if(条件2){条件2成立时内容 }else{所有条件不成立时内容 }#include <iostream>using namespace std;int main() {int age 10;if (age > 18) {c…

ShardingSphere再回首

概念&#xff1a; 连接&#xff1a;通过协议 方言及库存储的适配&#xff0c;连接数据和应用&#xff0c;关注多模数据苦之间的合作 增量&#xff1a;抓取库入口流量题提供重定向&#xff0c; 流量变形(加密脱敏)/鉴权/治理(熔断限流)/分析等 可插拔&#xff1a;微内核 DDL:cr…

OSPF 开放式最短路径优先协议

目录 技术产生原因&#xff1a;因为RIP存在不足 OSPF优点&#xff1a; RIPV2和OSPFV2比较&#xff1a; 相同点&#xff1a; 不同点&#xff1a; OSPF的结构化部署 --- 区域划分 区域划分的主要目的&#xff1a; 区域边界路由器 --- ABR &#xff1a; 区域划分的要求&am…

PyCharm Pro 2024:卓越的Python编辑开发工具,适用于Mac与Windows平台

PyCharm Pro 2024是一款专为Python开发者设计的强大编辑开发工具&#xff0c;无论是Mac还是Windows用户&#xff0c;都能从中受益良多。该软件凭借其出色的性能、丰富的功能和卓越的用户体验&#xff0c;成为Python编程界的翘楚。 作为一款高效的Python编辑器&#xff0c;PyCh…

【MySQL】索引篇

SueWakeup 个人主页&#xff1a;SueWakeup 系列专栏&#xff1a;学习技术栈 个性签名&#xff1a;保留赤子之心也许是种幸运吧 本文封面由 凯楠&#x1f4f8;友情提供 目录 本系列传送门 1. 什么是索引 2. 索引的特性 3. 索引的分类 4. 索引的优点及缺点 优点 缺点 5.…

绝地求生:AUG爆裂弹球黑货箱:街机动漫风格大家会喜欢吗?

大好&#xff0c;我闲游盒&#xff01; 4.10更新后&#xff0c;AUG的新成长型也出来了&#xff0c;更新后我觉得AUG变好用了一点&#xff0c;不知道大家有没有感觉出来&#xff1f; 宝箱概率 本期主角 AUG-爆裂弹球&#xff08;紫色配粉红色&#xff09; 本次的AUG我才升到5级…

arm内核驱动-中断

先介绍个东西 ctags 这个工具可以像keil一样在工程里查找跳转&#xff0c;帮我们找到我们想要的东西。 安装教程可以找到&#xff0c;这里只讲怎么用。 在工程目录&#xff08;包含所有你会用到的头文件等&#xff09;下&#xff0c;先加载这个命令&#xff0c;可能要等待…

每日一题---OJ题: 链表的回文结构

片头 嗨! 小伙伴们,大家好! 今天我们来一起学习这道OJ题--- 链表的回文结构 嗯...这道题好像不是很难,我们来分析分析 举个例子: 我们可以看到,上图中的两个链表都是回文结构: 即链表的回文结构是指一个链表中的结点值从前往后读和从后往前读都是一样的结构。也就是说&#xf…