Loading [MathJax]/jax/output/CommonHTML/jax.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >层序遍历?套模板就够了

层序遍历?套模板就够了

作者头像
程序员小饭
发布于 2021-02-03 04:17:40
发布于 2021-02-03 04:17:40
77200
代码可运行
举报
文章被收录于专栏:golang+phpgolang+php
运行总次数:0
代码可运行

学算法认准 GTAlgorithm,点击下方卡片即可搜索:

1.树的层序遍历

顾名思义,对于树型结构,层序遍历就是按层从上到下,每层按一定顺序对树的节点进行遍历。我们通过如图所示的二叉树进行说明:对于左边的二叉树,按层划分后可得到右边的分层结构。

二叉树及其层序遍历示意图

如果按照每层从左到右的遍历逻辑,这棵二叉树的层序遍历序列就是

。通过代码如何实现呢?一般地,我们利用队列

作为容器,按照如下逻辑进行遍历:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 0. 声明队列
queue<TreeNode*> q;
// 1. 将根节点加入队列
q.push(root);
// 2. 遍历队列中每个节点
while(!q.empty()) {
    TreeNode* cur = q.front();
    q.pop();
    // 3. 记录当前节点值
    vec.push(cur->val);
    // 4. 将左右孩子放入队列
    q.push(cur->left);
    q.push(cur->right);
}

同一层中的节点自左向右遍历是通过队列实现的:还是拿之前的例子来说,先将值为

的节点放入队列,然后将先左孩子

放入队列,再将右孩子

放入队列,由于队列是先进先出型结构,所以保证了值为

的节点要先于值为

的孩子处理;同样地,第三层节点放入队列的顺序依次为

,与之后的处理顺序相同,保证了从左向右的顺序。过程如下图所示:(黄色代表加入队列的节点、粉色代表处理完成的节点、蓝色表示等待加入队列的节点)

1

2

3

4

5

6

7

一般地,在遍历完第

层的最后一个节点后,该层所有节点都被弹出了队列,且其孩子节点(均处于

层)都被存入了队列且未处理,所以当前队列的长度就是

层的节点数量。

所以,通过提前记录队列长度,可以方便地应对一些需要对各层进行特殊处理的问题。

特别地,为了防止二叉树为空、遍历到叶节点等情况,需要加入一些特判元素。修改后的模板如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 1.初始化
queue<TreeNode*> q;
if(root == NULL) { // 二叉树为空
    return;
}
q.push(root);

// 2.遍历整棵树
while(1) {
    int cnt = q.size(); // 要处理层的节点个数
    if(cnt == 0) break; // 已经遍历完二叉树

    // 3.遍历该层
    while(cnt--) {
        TreeNode* cur = q.front();
        q.pop();

        // 4.对 cur 的操作,根据题意更改
        action(cur);

        // 5.将左右孩子放入队列
        if(cur->left) q.push(cur->left);
        if(cur->right) q.push(cur->right);
    }
}

2.几道例题

说明:对于不同的题目,只需要在我们的模板基础上增加或更改一些元素,所以对于下面的每道例题,我们在代码中只重点注释修改的部分。

第一题:二叉树的层序遍历

我们先从基础的力扣102题来入手:

题目要求返回一个二维容器,其中的每一个容器记录着某一层的所有节点值。我们只需要层序遍历二叉树,并按层遍历节点,将其加入

。在遍历完该层后,将记录了该层所有节点的

加入结果容器即可,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
vector<vector<int>> levelOrder(TreeNode* root) {
    // 声明结果二维容器
    vector<vector<int>> result;
    queue<TreeNode*> q;
    if(root == NULL) return result;
    q.push(root);
    while(1) {
        int cnt = q.size();
        if(cnt == 0) break;
        // 记录该层节点的容器
        vector<int> v;
        while(cnt--) {
            TreeNode* cur = q.front();
            q.pop();
            // 将当前节点存入容器
            v.push_back(cur->val);
            if(cur->left) q.push(cur->left);
            if(cur->right) q.push(cur->right);
        }
        // 处理完该层,加入结果容器
        result.push_back(v);
    }
    return result;
}

第二题:二叉树的层序遍历 II

与上一道题不同,要求从下到上遍历。实际上我们只需要从上向下遍历后,将结果容器翻转即可。C++的标准库STL给我们提供了容器翻转的函数:

第三题:二叉树的锯齿形层序遍历

与前两题不同,对于给定的如图所示的树,锯齿形遍历需要偶数层从右向左返回结果,奇数层从左向右返回结果,也即返回的结果序列应为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[
  [3],
  [20,9],
  [15,7]
]

本质上,我们只需要翻转偶数层的容器,就可以把从左向右的遍历转化为从右向左的遍历。在代码实现时,我们增加一个布尔型变量,记录当前层是否需要翻转,并每层将该变量取反即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
    vector<vector<int>> result;
    // 当前层是否需要翻转
    bool flag = false;
    queue<TreeNode*> q;
    if(root == NULL) return result;
    q.push(root);
    while(1) {
        int cnt = q.size();
        if(cnt == 0) break;
        vector<int> v;
        while(cnt--) {
            TreeNode* cur = q.front();
            q.pop();
            v.push_back(cur->val);
            if(cur->left) q.push(cur->left);
            if(cur->right) q.push(cur->right);
        }
        // 判断是否翻转
        if(flag) reverse(v.begin(), v.end());
        result.push_back(v);
        // 取反
        flag = !flag;
    }
    return result;
}

第四题:二叉树的最大深度

这是力扣第104题,看下题目:

在我们的模板里,每处理完一层,才退出内层循环,并开始新一轮外层循环。而本题要找最大深度,就是找一共处理了多少层,所以提前维护一个记录层数的变量

,然后在外层循环内每次增加该变量即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int maxDepth(TreeNode* root) {
    int depth = 0; // 声明深度
    queue<TreeNode*> q;
    if(root == NULL) return 0;
    q.push(root);
    while(1) {
        int cnt = q.size();
        if(cnt == 0) break;
        depth++; // 处理新一层前深度自加
        while(cnt--) {
            TreeNode* cur = q.front();
            q.pop();
            if(cur->left) q.push(cur->left);
            if(cur->right) q.push(cur->right);
        }
    }
    return depth;
}

特别地,

语句必须要放在判断

的语句之后,否则若遍历到最后一层,深度自加之后才会退出循环,导致结果错误。

第五题:二叉树的最小深度

第111题要求“最小深度”(找到离根节点最近的叶子节点),由于我们进行的是层序遍历,只要找到一个叶子节点,该节点就一定是所求的最近节点。所以,遍历过程中增加判断叶子节点的部分即可。来看看代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int minDepth(TreeNode* root) {
    int depth = 0;
    queue<TreeNode*> q;
    if(root == NULL) return 0;
    q.push(root);
    while(1) {
        int cnt = q.size();
        if(cnt == 0) break;
        depth++;
        while(cnt--) {
            TreeNode* cur = q.front();
            q.pop();
            // 叶子节点
            if(!cur->left && !cur->right) return depth;
            if(cur->left) q.push(cur->left);
            if(cur->right) q.push(cur->right);
        }
    }
    return depth;
}

最后总结

层序遍历的关键,要明确每一轮循环的具体过程。二叉树遍历相关的算法题,都是基于层序遍历框架的,我们只要搞清楚

节点本身要做什么,根据题目要求进行处理,再把左右孩子往队列里一放就行了。

如果本文讲的层序遍历对你有一些启发,请三连支持作者~~~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员养成日记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
每日一题(根据二叉树创建字符串,二叉树层序遍历,二叉树的层序遍历 II)
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第20天,点击查看活动详情
雪芙花
2022/10/31
1640
leetcode: 二叉树的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
利刃大大
2023/04/12
2140
LeetCode——二叉树的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。 示例 1:
有礼貌的灰绅士
2023/04/17
2020
LeetCode——二叉树的层序遍历
BFS:队列+树的宽搜
该题的层序遍历和以往不同的是需要一层一层去遍历,每一次while循环都要知道在队列中节点的个数,然后用一个for循环将该层节点走完了再走下一层
小陈在拼命
2024/06/28
600
BFS:队列+树的宽搜
每日一题:LeetCode-102.二叉树的层序遍历
   🔎🔎如果说代码有灵魂,那么它的灵魂一定是👉👉算法👈👈,因此,想要写出💚优美的程序💚,核心算法是必不可少的,少年,你渴望力量吗😆😆,想掌握程序的灵魂吗❓❗️那么就必须踏上这样一条漫长的道路🏇🏇,我们要做的,就是斩妖除魔💥💥,打怪升级!💪💪当然切记不可😈走火入魔😈,每日打怪,日日累积,终能成圣🙏🙏!开启我们今天的斩妖之旅吧!✈️✈️
用户11029129
2024/06/04
890
每日一题:LeetCode-102.二叉树的层序遍历
二叉树中的奇偶树问题
解答这道题,其实首先可以说是和leetcode上的另一道题相关,即二叉树的层序遍历:
用户11458826
2025/01/23
610
二叉树中的奇偶树问题
【C++】二叉搜索树
二叉搜索树又称二叉排序树,可以简写成 BST,它或者是一棵空树,或者是具有以下性质的二叉树:
YoungMLet
2024/03/01
1260
【C++】二叉搜索树
【算法】图论
_小羊_
2025/03/14
340
【算法】图论
leetcode刷题记录——2024年2月
使用队列进行层序遍历,sameparent记录每个节点的兄弟节点的值,同时sum用于存储当前遍历层的下一层节点总和。当遍历到下一层时,每个节点的val等于sum-兄弟节点的val。
Andromeda
2024/02/08
960
二叉树的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
狼啸风云
2024/01/28
1570
二叉树的层序遍历
超超超高频面试题,快来混个脸熟(一)
此份试题来自民间面经、code top 网站以及部分官方渠道,命中率超高,可以混个脸熟
ACM算法日常
2021/09/07
2500
LeetCode 742. 二叉树最近的叶节点(建立父节点信息+BFS)
给定一个 每个结点的值互不相同 的二叉树,和一个目标值 k,找出树中与目标值 k 最近的叶结点。
Michael阿明
2020/07/13
1.2K0
LeetCode 1161. 最大层内元素和(层序遍历)
给你一个二叉树的根节点 root。设根节点位于二叉树的第 1 层,而根节点的子节点位于第 2 层,依此类推。
Michael阿明
2020/07/13
3650
LeetCode 1161. 最大层内元素和(层序遍历)
LeetCode 102. 二叉树的层次遍历(BFS)
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
Michael阿明
2021/02/20
2710
LeetCode 102. 二叉树的层次遍历(BFS)
图解LeetCode第 103 号问题:二叉树的锯齿形层次遍历
返回其层次遍历结果: [ [3], [20,9], [15,7] ]
五分钟学算法
2018/12/25
8010
数据结构与算法面试题:实现二叉树的遍历(前序、中序、后序、层序)。
由于这是一道比较基础的二叉树问题,因此其实现思路也相对简单。但是在实际应用中需要灵活使用各种不同的遍历方式,并且代码的实现可能会涉及到栈和队列等相关数据结构。因此,熟悉常见的算法和数据结构、灵活掌握基础知识才是写好代码的关键。
GeekLiHua
2025/01/21
1090
【高效算法解析:从树的遍历到数据流处理的经典题解】—— LeetCode
用户11286421
2025/03/14
600
【高效算法解析:从树的遍历到数据流处理的经典题解】—— LeetCode
LeetCode 366. 寻找二叉树的叶子节点(上下翻转二叉树+BFS)
1. 题目 给你一棵二叉树,请按以下要求的顺序收集它的全部节点: 依次从左到右,每次收集并删除所有的叶子节点 重复如上过程直到整棵树为空 示例: 输入: [1,2,3,4,5] 1 / \ 2 3 / \ 4 5 输出: [[4,5,3],[2],[1]] 解释: 1. 删除叶子节点 [4,5,3] ,得到如下树结构: 1 / 2
Michael阿明
2020/07/13
1.5K0
二叉树oj以及前中后序非递归写法
给你二叉树的根节点 root ,请你采用前序遍历的方式,将二叉树转化为一个由括号和整数组成的字符串,返回构造出的字符串。
始终学不会
2023/10/17
2000
二叉树oj以及前中后序非递归写法
二叉搜索树在线OJ题讲解
我们首先进行题目的解读: 大概意思就是用()把每个节点的值给括起来,然后再经过一系列的省略的来得到最后的结果
ahao
2024/03/19
730
二叉搜索树在线OJ题讲解
推荐阅读
相关推荐
每日一题(根据二叉树创建字符串,二叉树层序遍历,二叉树的层序遍历 II)
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验