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

层序遍历?套模板就够了

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

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

1.树的层序遍历

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

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

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

[1,4,2,7,20,5]

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

queue

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

代码语言: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

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

4

放入队列,再将右孩子

2

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

4

的节点要先于值为

2

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

7,20,5

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

1

2

3

4

5

6

7

一般地,在遍历完第

k

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

k+1

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

k+1

层的节点数量。

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

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

代码语言: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题来入手:

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

vector

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

vector

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

代码语言: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给我们提供了容器翻转的函数:

reverse(result.begin(),result.end())

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

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

代码语言: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题,看下题目:

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

depth

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

代码语言: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;
}

特别地,

depth++

语句必须要放在判断

cnt=0

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

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

第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;
}

最后总结

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

root

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

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

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

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

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Manjaro21.0下MAC地址随机化
我们平时使用无线 Wifi 时,电脑的 IP 地址一般都是路由器分配的,因此这种情况下我们无法修改自己电脑的 IP 地址(除非路由器是你家的)。而我们电脑的 IP 地址有时候会被路由器莫名奇妙地限制,导致我们无法领略到互联网的精彩。(好吧,我不装了,我摊牌了,其实是我用自己电脑挖矿被校园网发现了,然后 IP 被禁了。。。可是我用的是自己的电脑啊喂,呜呜呜)
hotarugali
2022/02/28
5990
无线MAC地址过滤
无线MAC地址过滤功能通过MAC地址允许或拒绝无线网络中的计算机访问广域网,有效控制无线网络内用户的上网权限。
云深无际
2020/08/12
2.8K0
无线MAC地址过滤
Linux中MAC 地址欺骗具体方法
MAC地址欺骗(或MAC地址盗用)通常用于突破基于MAC地址的局域网访问控制,例如在交换机上限定只转发源MAC地址修改为某个存在于访问列表中的MAC地址即可突破该访问限制,而且这种修改是动态的并且容易恢复,本篇文章重点为大家讲解一下Linux中MAC 地址欺骗具体方法。
会长君
2023/04/26
2.3K0
49 张图 26 个问题详解 WiFi
以太网用 CSMA/CD 进行传输控制,而 IEEE 802.11 的 WLAN 采用的是 CSMA/CA 。
ICT系统集成阿祥
2024/12/03
2320
49 张图 26 个问题详解 WiFi
无线安全第一篇:如何攻破邻居的wife和防范
很多人有时候会问我怎么破解wife,关于无线网络这一块我也不是很懂,所以特意去了解了一下,然后结合网上的一些文章和教学,将会发出三篇有关这个方面的推送,这里将会用假人物带入,仅涉及技术交流,不做违法的事情,下面我们进入
网e渗透安全部
2019/08/09
3.7K0
无线安全第一篇:如何攻破邻居的wife和防范
【Linux】《how linux work》第九章 了解网络及其配置
Networking is the practice of connecting computers and sending data between them. That sounds simple enough, but to understand how it works, you need to ask two fundamental questions:
阿东
2024/04/22
2880
【Linux】《how linux work》第九章 了解网络及其配置
mmap及linux地址空间随机化失效漏洞
Linux下动态库是通过mmap建立起内存和文件的映射关系。其定义如下void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);,在第一个参数start为NULL的时候系统会随机分配一个地址,我们可以通过示例来看mmap映射地址的流程。
WeaponX
2018/09/20
2.3K0
Ubuntu20.04中遇到的网络问题
1. 已连上有线/无线,网络未开代理,却无法访问网络 缘由:我之前在 Ubuntu20.04 开过网络代理服务,当时访问网络正常。但今天突然把代理一关发现怎么都上不了网了,Ping 网络时报错名称解析服务失败,而奇怪的是一开代理又可以访问网络了。 解决:最终发现是 Ubuntu20.04 的网络名称解析服务即 systemd-resolved.service 未开启,因此导致无法由域名解析到 IP 地址,所以导致 Ping 网址域名的时候失败了。应该是由于我使用网络代理从而导致系统的网络名称解析服务被关闭了
hotarugali
2022/02/28
1.1K0
如何在 Ubuntu 15.04 下创建一个可供 Android/iOS 连接的无线AP热点
我成功地在 Ubuntu 15.04 下用 Gnome Network Manager 创建了一个无线AP热点。接下来我要分享一下我的步骤。请注意:你必须要有一个可以用来创建AP热点的无线网卡。如果你不知道如何确认它的话,在终端(Terminal)里输入iw list。
用户8704835
2021/06/08
8200
基于无线场景的本地MAC地址认证方案
MAC地址认证最早出现在有线交换机上面,对于接入交换机的口开启MAC认证,对应的终端MAC通过了数据才能正常通过这个口转发出去,随着无线应用的广泛,MAC地址认证也应用在了无线组网里面,在前几年可能应用的还是比较多,但随着智能手机的一些功能(一个叫做随机MAC的功能)出现、更多认证方式的出现、MAC地址认证带来的一些问题,导致MAC地址认证单独使用的场景越来越少了。
网络之路一天
2024/01/08
3250
基于无线场景的本地MAC地址认证方案
10个酷炫CMD命令
IP地址不用说了吧,那么如何查询本机IP呢?其实很简单,只要在命令行中输入“ipconfig”就可以了。这其实也是这条命令最常见的一种格式,此外它还包含几个特殊的后缀,比如“ipconfig /release”是释放本机现有IP,“ipconfig /renew”是向DHCP服务器(可以简单理解成你家的路由器)重新申领一个IP,“ipconfig /all”是显示完整版IP信息。
云深无际
2020/08/12
11.1K0
10个酷炫CMD命令
Ubuntu20.04linux内核(5.4.0版本)编译准备与实现过程-编译前准备(1)
最近项目也和linux kernel技术有关,调试内核和内核模块、修改内核源码,是学习内核的重要技术手段之一。应用这些技术时,都有一本基本的要求,那就是编译内核。因此,在分析内核调试技术之前,本随笔给出内核的编译准备工作与具体实现过程。
玖柒的小窝
2021/09/15
2.6K0
WIFI密码破解笔记
相对于前一段时间脆弱的WEP加密路由器而言,当今的路由器加密方式也大都改变为WPA/WPA2,使得无线路由器的破解难度增加。虽然如此,但还是有很多漏洞层出不穷,如针对路由器WPS的漏洞。退一步来说,即使加密算法无懈可击,我们还可以针对安全防护中最脆弱的部分——人——来进行破解。人的想象力实在是匮乏的很,往往设置密码来来回回就是那么几类,用一个常见的弱口令字典,往往就能在10分钟左右把其密码暴力破解出来。这里提供己种常见的WIFI破解方式,其中WEP破解因为已经过时,所以只是在提到的时候一笔带过,主要记录的是抓握手包然后暴力破解的通用方法,末尾也会提及到WPS的破解方式。注意这仅仅作为个人实验用,最好在自己的家庭网络中测试,以免给别人带来不便。
evilpan
2023/02/12
3.7K0
9个酷炫CMD命令
IP 地址不用说了吧,那么如何查询本机 IP 呢?其实很简单,只要在命令行中输入 ipconfig 就可以了。这其实也是这条命令最常见的一种格式,此外它还包含几个特殊... 1. ipconfig 功能:查询本机 IP 地址 IP 地址不用说了吧,那么如何查询本机 IP 呢?其实很简单,只要在命令行中输入 ipconfig 就可以了。这其实也是这条命令最常见的一种格式,此外它还包含几个特殊的后缀,比如 ipconfig /release 是释放本机现有 IP,ipconfig /renew是向 DHCP [
入门笔记
2022/06/03
1.6K0
9个酷炫CMD命令
关于终端设备的设备唯一性的那些事之MAC地址
最近和别人聊起来数据上报,一起讨论到imei和MAC地址,然后发现一个问题:知道这两个东西都不唯一,但是不知道为什么………… 回来上各种小网站巴拉巴拉找了一下,终于大概了解了前世今生,这里简单汇总一下MAC地址相关的内容。会在另一篇文章汇总imei相关的内容。链接如下: 关于终端设备的设备唯一性的那些事之IMEI 什么是MAC地址? MAC(Media Access Control或者Medium Access Control)地址,意译为媒体访问控制,或称为物理地址、硬件地址,用来定义网络设备的位置。在O
子勰
2018/05/22
3.4K0
无线攻防:wifi攻防从破解到钓鱼
无线安全是信息安全体系下一门很广泛的学科,包括但并不仅限于近场(NFC)、蓝牙(Bluetooth) 、射频(radio frequency,RF) 、无线局域网(Wifi)、手机蜂窝网络(cellular) 、卫星定位(GPS)等。
FB客服
2021/08/24
7.5K1
Archlinux基本安装
访问下载页面,根据您想要的启动方式,获取 ISO 文件或网络启动映像,以及相应的GnuPG签名。
vivi
2021/12/09
2K0
ESP32-C3 mqtt操作实践
对于ESP32-C3模块,是乐鑫的第一个基于RISCV架构的芯片,其主要定位还是一个物联网模块,所以在使用上更多的去考虑物联网的应用。本文主要是介绍在ESP32-C3模块上使用MQTT进行通信。
bigmagic
2021/05/18
3.2K0
ESP32-C3 mqtt操作实践
树莓派4b支持5gwifi吗_树莓派4和4b的区别
Manjaro Linux(或简称 Manjaro)是基于 Arch Linux 的 Linux 发行版,使用 Xfce 、GNOME和 KDE Plasma 作为默认桌面环境,和 Arch 一样,采用滚动更新。其目标是为 PC 提供易于使用的自由的操作系统。 Manjaro Linux 基于 Arch Linux,但拥有自己独立的软件仓库。Manjaro 的目标是让强大的 Arch 更方便用户使用,Manjaro 使用著名的 Pacman 且可以直接利用 AUR 上的资源。Manjaro 本身使用三个软件仓库:不稳定库,即含有那些不成熟的 Arch 包,这些包与 Arch 源有 1-2 天 的延后;测试库,每周同步一次,包含那些 Arch 不稳定源的包;以及稳定库,包含那些由开发团队确认稳定的软件。
全栈程序员站长
2022/11/01
1.5K0
树莓派4b支持5gwifi吗_树莓派4和4b的区别
通过 Hostapd 进行 WIFI 热点共享上网
最近发现自己的Debian之前可以使用GNOME3下的networkmanager进行WIFI共享上网功能因为内核升级导致无法使用。无奈只好再次通过Hostapd来进行WIFI热点设置,同时为了更块的DNS解析,本次顺便也在本地安装了dnsmasq软件实现了本地化的DNS查询服务,成功恢复了我的小本本作为热点的能力。总结方法如下:
Debian中国
2018/12/20
5.6K0
推荐阅读
相关推荐
Manjaro21.0下MAC地址随机化
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文