树的层次遍历

层次遍历简介

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

nginx部署以及反向代理多域名实现HTTPS访问

nginx部署以及反向代理多域名实现 1.nginx部署 1.1 编写nginx部署文件 docker-compose.yml version: 3 services: nginx:restart: always image: nginx:1.20container_name: nginx-mainports:- 80:80- 443:443volumes: # 基础配置- /opt/nginx_main/nginx-info/nginx.conf:/…

商城-学习整理-基础-商品服务API-品牌管理(六)

目录 一、使用逆向工程生成前后端代码1、新增品牌管理菜单2、使用生成的前端代码 二、优化逆向生成的品牌管理页面1、显示状态开关优化2、品牌上传优化&#xff08;使用阿里云存储&#xff09;1&#xff09;阿里云对象存储-普通上传方式2&#xff09;阿里云对象存储-服务端签名…

Linux系统部署Python语言开发运行环境

目录 Ubuntu自带python Debian安装python 安装 pip 库列表 安装第三方库 使用国内镜像站 实装 tkinter 库 编写运行代码 测试代码1 1. 创建项目 2. 创建源码文件 3. 写入源代码 4. 修改权限 5. 运行代码 测试代码2 本文的使用环境是Windows的Linux 子系统&…

基于fpga_EP4CE6F17C8_秒表计数器

文章目录 前言实验手册一、实验目的二、实验原理1&#xff0e;理论原理2&#xff0e;硬件原理 三、系统架构设计四、模块说明1&#xff0e;模块端口信号列表dig_driver(数码管驱动模块)key(按键消抖模块)top(顶层模块) 2&#xff0e;状态转移图3&#xff0e;时序图五、仿真波形…

Windows 本地安装 Mysql8.0

前言 看了网上许多关于Windows 本地安装mysql的很多教程&#xff0c;基本上大同小异。但是安装软件有时就可能因为一个细节安装失败。我也是综合了很多个教程才安装好的&#xff0c; 所以本教程可能也不是普遍适合的。现我将自己本地安装的步骤总结如下&#xff0c;如有不对的…

高级web前端开发工程师的职责说明(合集)

高级web前端开发工程师的职责说明1 职责&#xff1a; 1、根据需求文档&#xff0c;完成PC端、移动端页面及交互的开发&#xff0c;并保证兼容性和确保产品具有优质的用户体验; 2、熟练使用 HTML 、 CSS 、 JS 、 Ajax 等技术&#xff0c;能解决各种浏览器兼容性问题&#xff…

boost beast http server 测试

boost beast http client boost http server boost beast 是一个非常好用的库&#xff0c;带上boost的协程&#xff0c;好很多东西是比较好用的&#xff0c;以下程序使用四个线程开启协程处理进入http协议处理。协议支持http get 和 http post #include <boost/beast/cor…

关于盐雾试验

盐雾实验一般被称为盐雾试验&#xff0c;是一种主要利用盐雾试验设备所创造的人工模拟盐雾环境条件来考核产品或金属材料耐腐蚀性能的环境试验。 盐雾实验的主要目的是考核产品或金属材料的耐盐雾腐蚀性能&#xff0c;盐雾试验结果也是对产品质量的判定&#xff0c;是正确衡量…

mongodb-win32-x86_64-2008plus-3.4.24-signed.msi

Microsoft Windows [版本 6.1.7601] 版权所有 (c) 2009 Microsoft Corporation。保留所有权利。C:\Users\Administrator>cd C:\MongoDB\Server\3.4\binC:\MongoDB\Server\3.4\bin>C:\MongoDB\Server\3.4\bin>mongod --help Options:General options:-h [ --help ] …

UML-构件图

目录 1.概述 2.构件的类型 3.构件和类 4.构件图 1.概述 构件图主要用于描述各种软件之间的依赖关系&#xff0c;例如&#xff0c;可执行文件和源文件之间的依赖关系&#xff0c;所设计的系统中的构件的表示法及这些构件之间的关系构成了构件图 构件图从软件架构的角度来描述…

Docker极速安装Jenkins

安装 Jenkins 是一个常见的任务&#xff0c;使用 Docker 进行安装可以简化该过程并确保环境一致性。以下是在 Docker 中安装 Jenkins 的详细步骤&#xff1a; 安装 Docker: 首先&#xff0c;请确保您已在目标机器上安装了 Docker。根据您的操作系统&#xff0c;可以在 Docker 官…

【Spring框架】Spring事务

目录 Spring中事务的实现编程式事务声明式事务Transactional 作⽤范围Transactional 参数说明注意事项Transactional ⼯作原理 MySQL 事务隔离级别Spring 事务隔离级别事务传播机制 Spring中事务的实现 Spring中事务操作分为两类&#xff1a; 1.编程式事务 2.声明式事务 编程…

meanshift算法通俗讲解【meanshift实例展示】

meanshift算法原理 meanshift算法的原理很简单。假设你有一堆点集&#xff0c;还有一个小的窗口&#xff0c;这个窗口可能是圆形的&#xff0c;现在你可能要移动这个窗口到点集密度最大的区域当中。 如下图&#xff1a; 最开始的窗口是蓝色圆环的区域&#xff0c;命名为C1。蓝…

一百四十四、Kettle——Linux上安装的kettle8.2连接MySQL数据库

一、目的 在Linux上安装好kettle&#xff0c;然后用kettle连接MySQL数据库 注意&#xff1a;kettle版本是8.2 二、实施步骤 &#xff08;一&#xff09;到kettle安装目录下启动Linux的kettle服务 # cd /opt/install/data-integration/ # ./spoon.sh &#xff08;二&#x…

SpringCloud深度学习(在更)

微服务简介 微服务是什么&#xff1f; 微服务是一种架构风格&#xff0c;将一个大型应用程序拆分为一组小型、自治的服务。每个服务都运行在自己独立的进程中&#xff0c;使用轻量级的通信机制&#xff08;通常是HTTP或消息队列&#xff09;进行相互之间的通信。这种方式使得…

springboot-mybatis的增删改查

目录 一、准备工作 二、常用配置 三、尝试 四、增删改查 1、增加 2、删除 3、修改 4、查询 五、XML的映射方法 一、准备工作 实施前的准备工作&#xff1a; 准备数据库表 创建一个新的springboot工程&#xff0c;选择引入对应的起步依赖&#xff08;mybatis、mysql驱动…

TP DP PP 并行训练方法介绍

这里写目录标题 张量并行TP流水线并行 PPnaive模型并行GPipePipeDream 数据并行DPFSDP 张量并行TP 挖坑 流水线并行 PP 经典的流水线并行范式有Google推出的Gpipe&#xff0c;和微软推出的PipeDream。两者的推出时间都在2019年左右&#xff0c;大体设计框架一致。主要差别为…

细讲一个 TCP 连接能发多少个 HTTP 请求(二)

第三个问题&#xff1a;一个 TCP 连接中 HTTP 请求发送可以一起发送么&#xff08;比如一起发三个请求&#xff0c;再三个响应一起接收&#xff09;&#xff1f; HTTP/1.1 存在一个问题&#xff0c;单个 TCP 连接在同一时刻只能处理一个请求&#xff0c;意思是说&#xff1a;两…

[Docker实现测试部署CI/CD----相关服务器的安装配置(2)]

目录 6、Jenkins安装配置安装jdk安装maven拉取镜像启动jenkins修改数据卷权限浏览器访问安装插件配置jenkins移动JDK和Maven配置JDK和Maven 6、Jenkins安装配置 Jenkins 是一个开源软件项目&#xff0c;是基于 Java 开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&…

js省市区下拉框联动——前端笔记

问题&#xff1a; 我们常常要用到下拉框联动的功能&#xff0c;比如最常用的是选择地址的 省 市 区 的联动。思路&#xff1a; 先填充第一个下拉框&#xff0c;然后写一个第一个下拉框的change事件来加载第二个下拉框&#xff0c;再写第二个下拉框的change事件来加载第三个下…