树的层次遍历

news2024/11/16 4:30:47

层次遍历简介

        广度优先在面试里出现的频率非常高,整体属于简单题。而广度优先遍历又叫做层次遍历,基本过程如下:

image.png

        层次遍历就是从根节点开始,先访问根节点下面一层全部元素,再访问之后的层次,类似金字塔一样,逐层访问。我们可以看到上面例子就是从左到右一层一层遍历二叉树,先访问3,之后访问1的左右孩子9和10,之后分别访问9和20的左右孩子[4,5]和[6,7] ,最后得到结果[3,9,20,8,13,15,7]

        这里需要关注的问题是:将遍历过的元素的左右孩子保存起来。例如:访问9时,其左右孩子8和13应该先保存一下,直到处理20之后才会处理。使用队列就是一个比较好的方法。

        以上图为例,过程如下

  1. 先将3入队(使用链表实现的队列)
  2. 然后3出队,接着将3的左右孩子9和10  保存在队列中
  3. 然后9 出队,将9的左右孩子8和13入队
  4. 接着20出队,将15和7入队
  5. 最后8  13  15  7  分别出队,此时都是叶子节点了,没有入队的元素了。 

拓展

如果能将树的每层次分开了,是否可以整点新花样?

        首先,能否将每层的元素顺序给反转一下呢?

        能否奇数行不变,只将偶数行反转呢?

        能否将输出层次从低到root逐层输出呢?

        再来,既然能拿到每一层的元素了,能否找到当前层最大的元素? 最小的元素? 最右的元素(右视图)? 最左的元素(左视图) ? 整个层的平均值? 

而以上问题就是经典的层次遍历题。

题号如下

102.二又树的层序遍历
107.二又树的层次遍历lIl
199.二又树的右视图
637.二又树的层平均值
429.N又树的前序遍历
515.在每个树行中找最大值
116.填充每个节点的下一个右侧节点指针
117.填充每个节点的下一个右侧节点指针Il
103 锯齿层序遍历 

基本的层序遍历与变换

题目

        遍历并输出全部元素,二叉树示例如下:

*              3
*            /   \
*          9      20
*                 /  \
*               15   7

代码实现思路

         先访问根节点,然后将其左右孩子放到队列里,接着继续出队列;出来的元素,将其左右孩子放入队列中,直到队列为空了才退出来。

代码实现

节点代码

public TreeNode{
    public int data;
    public TreeNode left;
    public TreeNode right;
    public TreeNode(int data){
        this.data = data;
    }
}

实现代码

public List<Integer> simpleLevelTraverse(TreeNode root){
    // 创建一个数组实现的集合接收遍历后的结果
    List<Integer> res = new ArrayList<>();
    // 如果该树是一颗空树,返回空的集合
    if(root == null){
        return res;
    }
    // 使用链表当做队列,用于存储树的节点
    // 从尾插入,从头删除
    LinkedList<TreeNode> queue = new LinkedList<>();
    // 将根节点放入队列中,然后不断遍历根节点
    queue.add(root);
    // 有多少元素就遍历多少次
    while(queue.size() > 0){
        // remove():获取并移除此队列的头(第一个元素)
        TreeNode t = queue.remove();
        // 使用集合搜集遍历后的节点数据部分
        res.add(t.data);
        // 如果该节点有左节点(不为空),则将其加入队列中,用于下次遍历
        if(t.left != null){
            queue.add(t.left);
        }
        // 如果该节点有右节点(不为空),则将其加入队列中,用于下次遍历
        if(t.right != null){
             queue.add(t.right);
        }
    }
    // 返回遍历后的结果
    return res;

}

         根据树的结构可以看到,一个节点在一层访问之后,其子孩子都是在下一层按照FIFO的顺序处理的,因此,队列就相当于一个缓冲区。这题是逐层自左向右执行的,如果将每层的元素分开打印,便有了另一种题目。

二叉树的层序遍历(每层元素分开打印)

题目

        给你一个二叉树,请你返回其按照程序遍历得到的节点值。(即逐层从左到右遍历)

示例

     * 二叉树:[3,9,20,null,null,15,7]
     *              3
     *            /   \
     *          9      20
     *                 /  \
     *               15   7
*        返回后程序遍历结果
     *        [
     *        [3],
     *        [9,20],
     *        [15,7]
     *        ]

 解析

        此题依旧可以使用队列存储每一层的节点。但题目要求逐层节点打印,那就需要考虑某一层是否访问完。针对这个问题,可以使用一个变量size标记一下某一层的元素个数,只要出队列一个节点,就将size的值减1,减到0,说明该层元素访问完了(当然,也可以从0开始遍历,遍历到size个的时候(i < size)就说明这一层的节点都访问过了),然后重新将size标记为下一层节点的个数,继续执行(因此,size需要在循环中创建)。

        对于结果集,可以看作外层一个集合(用于存储层数),里面一层集合(用于存储每一层的节点值)

代码实现 

public List<List<Integer>> getEveryLevelNodeFromTop(TreeNode root){
    List<List<Integer>> res = new ArrayList<List<Integer>>>();
    if(root == null){
        return res;
    }
    LinkedList<TreeNode> queue = new LinkedList<>();
    queue.add(root);
    while(queue.size()>0){
        List<Integer> temp = new  ArrayList<>();
        int size = queue.size();   
        for(int i = 0 ; i < size ; i++){
            TreeNode t = queue.remove();
            temp.add(t.data);
            if(t.left != null){
                queue.add(t.left);
            }
            if(t.right != null){
                queue.add(t.right);
            }
        }
        res.add(temp);
    }
    return res;
}

重要性

         上面的代码是整个算法体系的核心算法之一,与链表反转、二分查找属于同一个级别!

层序遍历(自底向上)

题目

        给定一个二叉树,返回其节点值自底向上的层序遍历。(即:按从叶子节点所在层到根节点所在的层,逐层自左向右遍历)

*              3
*            /   \
*          9      20
*                 /  \
*               15   7

示例

[

        [15,7],

        [9,20],

        [3]

]

分析

        如果要求从上到下输出每一层的节点值,做法是很直观的。在遍历完一层节点之后,将存储该层节点值的列表添加到结果列表的尾部。这道题要求从下到上输出每一层的节点值,只要对上述操作稍作修改即可在遍历完一层节点之后,将存储该层节点值的列表添加到结果列表的头部。 

        为了降低在结果列表的头部添加一层节点值的列表的时间复杂度,结果列表可以使用链表的结构,在链表头部添加一层节点值的列表的时间复杂度是 O(1)。在 Java 中,由于我们需要返回的 List 是一个接口,这里可以使用链表实现。

代码实现 

public List<List<Integer>> getEveryNodeFromBottom(TreeNode root){
    List<List<Integer>> res = new LinkedList<List<Integer>>();
    if(root == null){
        return res;
    }
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);
    while(! queue.isEmpty()){
        List<Integer> temp = new ArrayList<>();
        int size = queue.size();
        for(int i = 0 ; i < size ; i++){
            TreeNode t = queue.pull();
            temp.add(t.data);
            TreeNode left = t.left , right = t.right;
            if(left != null){
                queue.offer(left);
            }
            if(right != null){
                queue.offer(right);
            }
        }
        // 栈结构:将先进入结果集的集合放入后面
        res.add(0,temp);
    }
    return res;
}

二叉树的锯齿形遍历

题目

给定一个二叉树,返回其节点值的锯齿形层序遍历(即:先从左往右,再从右往左进行下一层遍历。以此类推,层与层之间交替执行)

*              3
*            /   \
*          9      20
*                 /  \
*               15   7

示例

[

        [3],

        [20,9],

        [15,7]

]

分析

        这个题与之前的区别只是最后输出的要求有所变化,要求我们按层数的奇偶来决定每一层的输出顺序如果当前层数是偶数,从左至右输出当前层的节点值,否则,从右至左输出当前层的节点值。为了满足题目要求的返回值为[先从左往右,再从右往左]交替输出的锯齿形,可以利用[双端队列,的数据结构来维护当前层节点值输出的顺序。双端队列是一个可以在队列任意一端插入元素的队列。在广度优先搜索遍历当前层节点拓展下一层节点的时候我们仍然从左往右按顺序拓展,但是对当前层节点的存储我们维护一个变量 isOrderLeft 记录是从左至右还是从右至左的:
        如果从左至右,我们每次将被遍历到的元素插入至双端队列的末尾。
        从右至左,我们每次将被遍历到的元素插入至双端队列的头部。

代码实现

  /**
     * 给定一个二叉树,返回其节点值的锯齿形层序遍历(即:先从左往右,再从右往左进行下一层遍历。以此类推,层与层之间交替执行)
     * @param root 树的根节点
     * @return 交替执行的遍历结果
     */
    public List<List<Integer>> levelOrderBetweenLeftAndRight(TreeNode root){
        LinkedList<List<Integer>> res = new LinkedList<List<Integer>>();
        if (root == null){
            return res;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        boolean isOrderLeft = true;
        while (!queue.isEmpty()){
            Deque<Integer> levelLeft = new LinkedList<Integer>();
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode curNode = queue.remove();
                if (isOrderLeft){
                    levelLeft.offerLast(curNode.data);
                }else {
                    levelLeft.offerFirst(curNode.data);
                }
                if (curNode.left != null){
                    queue.add(curNode.left);
                }
                if (curNode.right != null) {
                     queue.add(curNode.right);
                  }
                }
                res.add(new LinkedList<>(levelLeft));
                isOrderLeft = !isOrderLeft;
            }
        return res;
        }

层次遍历(N叉树) 

 题目

        给定一个 N 又树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)树的序列化输入是用层序遍历,每组子节点都由 null 值分隔 (参见示例)。

 示例

输入: root = [1,null,3,2,4,nu11,5,6](表述树的元素是这个序列)

输出: [[1],[3,2,4],[5,6]]

代码实现

N叉树代码

class MutilTreeNode {
    public int data ;
    public List<MutilTreeNode> children;
}

实现 


        public List<List<Integer>> nLevelOrder(MutilTreeNode root){
            ArrayList<List<Integer>> res = new ArrayList<>();
            Deque<MutilTreeNode> deque = new LinkedList<>();
            if (root != null){
                //Java中的java.util.LinkedList.addLast()方法用于在LinkedList的末尾插入特定元素。
                deque.addLast(root);
            }
            while (! deque.isEmpty()){
                ArrayList<Integer> nd = new ArrayList<>();
                ArrayDeque<MutilTreeNode> next = new ArrayDeque<>();
                while (! deque.isEmpty()){
                    MutilTreeNode cur = deque.pollFirst();
                    nd.add(cur.data);
                    for (MutilTreeNode child : cur.children) {
                        if (child != null){
                            next.add(child);
                        }
                    }
                    deque = next;
                    res.add(nd);
                }
            }
            return res;
        }

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VMware 识别移动硬盘,以及读取硬盘里的文件

一. 识别移动硬盘 右键“我的电脑”->管理->服务-> VMware USB Arbitration Service-> 属性->启动 重启VMware&#xff0c;可移动设备->移动硬盘->连接 二.读取硬盘里的文件 跟上一步没有关联 打开这个设置 依次点【选项】--》共享文件夹&#xff0c;…

16-1_Qt 5.9 C++开发指南_多语言界面

文章目录 1. 多语言界面设计概述2. tr()函数的使用3. 生成语言翻译文件4. 使用Qt Linguist 翻译 ts 文件5. 调用翻译文件改变界面语言5.1 生成qm文件5.2 项目启动时设置界面语言5.3 动态切换语言 1. 多语言界面设计概述 有些软件需要开发多语言界面版本&#xff0c;如中文版和…

HCIP---OSPF的优化

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 一.汇总&#xff1a; 目的&#xff1a;减少骨干区域的LSA的更新量 作用&#xff1a;OSPF的…

设计模式概述与UML图

文章目录 一、设计模式概述1. 软件设计模式的产生背景2. 软件设计模式的概念3. 学习设计模式的必要性4. 设计模式分类&#xff08;1&#xff09;创建型模式&#xff08;2&#xff09;结构型模式&#xff08;3&#xff09;行为型模式 二、UML图1. 类图概述2. 类图作用3. 类图表示…

freeswitch的mod_xml_curl模块动态获取dialplan

概述 freeswitch是一款简单好用的VOIP开源软交换平台。 mod_xml_curl模块支持从web服务获取xml配置&#xff0c;本文介绍如何动态获取dialplan配置。 环境 centos&#xff1a;CentOS release 7.0 (Final)或以上版本 freeswitch&#xff1a;v1.6.20 GCC&#xff1a;4.8.5…

十四.redis哨兵模式

redis哨兵模式 1.概述2.测试3.哨兵模式优缺点 redis哨兵模式基础是主从复制 1.概述 主从切换的技术方法&#xff1a;当主节点服务器宕机后&#xff0c;需要手动把一台从服务器切换为主服务器&#xff0c;这就需要人工干预&#xff0c;费时费力&#xff0c;还会造成一段时间内服…

MySQL进阶--性能分析

目录 一、简介二、什么的SQL性能分析三、性能分析的方式1.SQL执行频率2.慢查询日志3.profile详情4.explain执行计划 PS: 一、简介 本文的内容讲的是MySQL的性能分析&#xff0c;包括执行频率、慢查询日志、profile详情和explain执行计划~ 二、什么的SQL性能分析 SQL性能分析…

【分布式系统】前言

争取写一下阅读笔记&#xff0c;更新有关分布式系统的一切&#xff0c;先开个坑。 现在的心得如下&#xff1a; 不知道啥时候能破解哈&#xff5e;&#xff5e; 内容包括部分6.824 读的论文 DDIA&#xff1a; DDIA mapreduce GFS VMwareFT Raft zookeeper chain replication…

STM32——STM32F401x系列标准库的下载+环境搭建+建工程步骤(更完整)

文章目录 标准库的下载环境搭建建工程最后的话 标准库的下载 1.STM32标准库的官网下载网站https://www.st.com/content/st_com/en.html 2. 3. 4. 5. 6. 7.点击之后下滑 8.选择自己需要的版本下载 环境搭建建工程 大致步骤同之前我写的一篇STM32——建工程差不多&#xff0…

【ChatGPT 指令大全】怎么使用ChatGPT写履历和通过面试

目录 怎么使用ChatGPT写履历 寻求履历的反馈 为履历加上量化数据 把经历修精简 为不同公司客制化撰写履历 怎么使用ChatGPT通过面试 汇整面试题目 给予回馈 提供追问的问题 用 STAR 原则回答面试问题 感谢面试官的 email 总结 在职场竞争激烈的今天&#xff0c;写一…

3d 地球与卫星绕地飞行

1 创建场景 2 创建相机 3 创建地球模型 4 创建卫星中心 5 创建卫星圆环及卫星 6 创建控制器 7 创建渲染器 <template><div class"home3dMap" id"home3dMap"></div> </template><script> import * as THREE from three impo…

git之reflog分析

写在前面 本文一起看下reflog命令。 1&#xff1a;场景描述 在开发的过程中&#xff0c;因为修改错误&#xff0c;想要通过git reset命令恢复到之前的某个版本&#xff0c;但是选择提交ID错误&#xff0c;导致多恢复了一个版本&#xff0c;假定&#xff0c;该版本对应的内容…

【rust/入门】windows安装rust gnu环境(折腾)

说在前面 首先说明&#xff0c;我是rust入门选手&#xff0c;之前都是在wsl写rust&#xff0c;突然想在windows下装下rust。windows版本&#xff1a;windows11 22H2原文换源 心路历程 看到教程我陷入了沉默&#xff0c;(官方推荐) 打开Microsoft C Build Tools我开始不解&…

三 动手学深度学习v2 —— Softmax回归+损失函数+图片分类数据集

三 动手学深度学习v2 —— Softmax回归损失函数图片分类数据集 目录: softmax回归损失函数 1. softmax回归 回归vs分类: 回归估计一个连续值分类预测一个离散类别 从回归到多类分类 回归 单连续数值输出自然区间R跟真实值的误差作为损失 分类 通常多个输出输出i是预测为第…

Jmeter组件作用域及执行顺序

目录 一、Jmeter八大可执行元件 二、组件执行顺序 三、组件作用域 四、特殊说明 一、Jmeter八大可执行元件 配置元件---Config Element 用于初始化默认值和变量&#xff0c;以便后续采样器使用。配置元件大其作用域的初始阶段处理&#xff0c;配置元件仅对其所在的测试树分…

使用ubuntu-base制作根文件系统

1&#xff1a;ubuntu官网下载最小根文件系统&#xff1a; 放置到电脑的ubuntu中&#xff0c; Mkdir Ubuntu_rootfs Cd Ubuntu_rootfs Sudo tar –zxvf Ubuntu-bash-xxxxxx.tar.gz 2&#xff1a;电脑的ubuntu安装qemu搭建arm模拟系统 将/usr/bin/qemu-arm-static/(64位拷贝…

zookeeper --- 基础篇

一、zookeeper简介 1.1、什么是zookeeper zookeeper官网&#xff1a;https://zookeeper.apache.org/ 大数据生态系统里的很多组件的命名都是某种动物或者昆虫&#xff0c;他是用来管 Hadoop&#xff08;大象&#xff09;、Hive(蜜蜂)、Pig(小 猪)的管理员。顾名思义就是管理…

openpnp - 8mm物料编带的厚度

文章目录 openpnp - 8mm物料编带的厚度概述笔记END openpnp - 8mm物料编带的厚度 概述 做了一个散料飞达, 回来后试了一下. 并不是所有8mm编带都能顺利插入散料飞达. 原来, 不同物料的8mm编带厚度是不一样的. 那就量一下. 笔记 参考电阻的厂家说明书(e.g. C2907561_贴片电阻…

车载总线系列——J1939三

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 没有人关注你。也无需有人关注你。你必须承认自己的价值&#xff0c;你不能站在他人的角度来反对自己。人…

【Java】如何判断线程池任务执行完?

文章目录 前言1.需求分析2.实现概述3.具体实现3.1 统计完成任务数3.2 FutureTask3.3 CountDownLatch和CyclicBarrier 小结 前言 论是在项目开发中&#xff0c;还是在面试中过程中&#xff0c;总会被问到或使用到并发编程来完成项目中的某个功能。 例如某个复杂的查询&#xf…