【数据结构】二叉树(二)遍历

news2024/9/23 5:18:56

上篇已经了解对二叉树有了大概了解,本篇学习二叉树的前序、中序、后序及层序遍历的递归与非递归共7种遍历方法,快收藏吧~

目录

1、前序遍历

递归方式:

迭代方式: 

2、中序遍历

递归方式:

 迭代方式:

3、后序遍历:

递归方式

迭代方式:

4、层序遍历

递归方式

迭代方式: 


二叉树的前序、中序、后序及层序遍历的递归与非递归遍历方法

遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结 点均做一次且仅做一次访问访问结点所做的操作依赖于具体的应用问题(比如:打印节点内容、节点内容加 1)。 遍历是二叉树上最重要的操作之一,是二叉树上进行其它运算之基础。

二叉树的遍历方法有以下四种 :

  • 先序遍历(根左右)

        首先访问根结点,然后递归地遍历左子树,最后遍历右子树。例如对于二叉树:先序遍历的结果为:1->2->4->8->9->5->10->11->3->6->7。

  • 中序遍历(左根右)

        首先遍历左子树,然后访问根结点,最后遍历右子树。例如对于二叉树:中序遍历的结果为:8->4->9->2->10->5->11->1->6->3->7。

  • 后序遍历(左右根)

        首先遍历左子树,然后遍历右子树,最后访问根结点。例如对于二叉树:后序遍历的结果为:8->9->4->10->11->5->2->6->7->3->1。

  • 层序遍历

   自上而下,自左至右逐层访问树的结点的过程就是层序遍历。      

  它是按广度优先搜索的策略,从根结点出发,依次访问每一层上的节点。这种策略在实际应用中使用较多,如在计算机图形学中用于渲染场景图等。例如对于二叉树:层次遍历的结果为:1->2->3->4->5->6->7->8->9->10->11

下面讲解代码,前序遍历、中序遍历和后序遍历的简单递归方法不附图讲解,主要解释它们的迭代方式及层序遍历


1、前序遍历

递归方式:

定义 preorder(root) 表示当前遍历到 root 节点的答案。按照定义,我们只要首先将 root 节点的值加入答案,然后递归调用 preorder(root.left) 来遍历 root 节点的左子树,最后递归调用 preorder(root.right) 来遍历 root 节点的右子树即可,递归终止的条件为碰到空节点。

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        preorder(root, res);
        return res;
    }

    public void preorder(TreeNode root, List<Integer> res) {
        if (root == null) {
            return;
        }
        res.add(root.val);
        preorder(root.left, res);
        preorder(root.right, res);
    }
}

复杂度分析

时间复杂度:O(n)其中 n 是二叉树的节点数。每一个节点恰好被遍历一次。

空间复杂度:O(n),为递归过程中栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)。

迭代方式: 

迭代的方式实现方法一的递归函数,两种方式是等价的,区别在于递归的时候隐式地维护了一个栈,而我们在迭代的时候需要显式地将这个栈模拟出来,其余的实现与细节都相同,具体可以参考下面的代码。

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if(root==null) return res;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode p = stack.pop();
            res.add(p.val);
            if(p.right!=null)
            stack.push(p.right);
            if(p.left!=null
            )stack.push(p.left);
        }
        return res;
    }
}

注释: 先将root压栈,进入 while 循环,每循环一次栈顶出栈一次并记录该结点,将其左结点右结点压栈,栈为空退出循坏。

时间复杂度:O(n),其中 n 是二叉树的节点数。每一个节点恰好被遍历一次。

空间复杂度:O(n),为迭代过程中显式栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)。


2、中序遍历

递归方式:

定义 inorder(root) 表示当前遍历到 root 节点的答案,那么按照定义,我们只要递归调用 inorder(root.left) 来遍历 root 节点的左子树,然后将 root 节点的值加入答案,再递归调用inorder(root.right) 来遍历 root 节点的右子树即可,递归终止的条件为碰到空节点。

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        inorder(root, res);
        return res;
    }

    public void inorder(TreeNode root, List<Integer> res) {
        if (root == null) {
            return;
        }
        inorder(root.left, res);
        res.add(root.val);
        inorder(root.right, res);
    }
}

复杂度分析

时间复杂度:O(n),其中 n 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。

空间复杂度:O(n)。空间复杂度取决于递归的栈深度,而栈深度在二叉树为一条链的情况下会达到 O(n) 的级别

 迭代方式:

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<>();
        while (root != null || !stack.isEmpty()) {
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            res.add(root.val);
            root = root.right;
        }
        return res;
    }
}

子循环中root遍历到root左子树最深层次过程中,依次将左结点压栈,左结点为空退出子循环,将栈顶元素出栈, root=root.right重新进入大循环既可遍历该树所有结点。

时间复杂度:O(n),其中 n 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。

空间复杂度:O(n)。空间复杂度取决于栈深度,而栈深度在二叉树为一条链的情况下会达到 O(n) 的级别。 


3、后序遍历

递归方式

定义 postorder(root) 表示当前遍历到 root 节点的答案。按照定义,我们只要递归调用 postorder(root->left) 来遍历 root 节点的左子树,然后递归调用 postorder(root->right) 来遍历 root 节点的右子树,最后将 root 节点的值加入答案即可,递归终止的条件为碰到空节点。

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        postorder(root, res);
        return res;
    }

    public void postorder(TreeNode root, List<Integer> res) {
        if (root == null) {
            return;
        }
        postorder(root.left, res);
        postorder(root.right, res);
        res.add(root.val);
    }
}

复杂度分析

时间复杂度:O(n),其中 n 是二叉搜索树的节点数。每一个节点恰好被遍历一次。

空间复杂度:O(n),为递归过程中栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)。

迭代方式:

我们也可以用迭代的方式实现方法一的递归函数,两种方式是等价的,区别在于递归的时候隐式地维护了一个栈,而我们在迭代的时候需要显式地将这个栈模拟出来,其余的实现与细节都相同,具体可以参考下面的代码(为了更好的注释,代码放入块引用中)

class Solution {

    public List<Integer> postorderTraversal(TreeNode root) {

        List<Integer> res = new ArrayList<Integer>();

        if (root == null) {

            return res;

        }

        Stack <TreeNode> stack = new Stack<>();

        TreeNode prev = null;

        while (root != null || !stack.isEmpty()) {

            while (root != null) {

                stack.push(root);

                root = root.left;

            }

            root = stack.peek();//  取栈顶元素

//因为是后序遍历,当前结点有无右结点决定它是否打印

//所以取出栈顶元素,判断有无右结点

         //而打印当前结点情况分为两种,1、该结点无右结点  2、该结点的右结点已遍历

      if (root.right == null || root.right == prev) {

                res.add(root.val);

                stack.pop();

                prev = root;//用prev指向root,表示已被遍历

                root = null;

            } else 

                root = root.right;

        }

        return res;

    }

}

如上图情况,若if条件语句不判断 D.right ( K )是否被上次遍历则会陷入K出栈压栈无限循环中

复杂度分析 

时间复杂度:O(n),其中 n 是二叉搜索树的节点数。每一个节点恰好被遍历一次。

空间复杂度:O(n),为迭代过程中显式栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)。


4、层序遍历

即逐层地,从左到右访问所有节点。

递归方式

class Solution {
    List<List<Integer>> result=new ArrayList<>();
    public List<List<Integer>> levelOrder(TreeNode root) {
        order(root,0);
        return result;
    }
    public void order(TreeNode node,int deep)
    {
        if(node==null)return;
        deep++;
        if(result.size()<deep)
        {
            List<Integer> item=new ArrayList<>();
            result.add(item);
        }
        result.get(deep-1).add(node.val);
        order(node.left,deep);
        order(node.right,deep);
    }
}

在方法void order(TreeNode node,int deep) ,传相应的结点和二维数组下标,设置节点对应位置

加图理解: 

时间复杂度;O(n) 

迭代方式: 

代码设计重点:出栈既遍历。在结点出栈时,同时将不为空的左右结点入栈。进入子循环前,用size记录栈内元素,进入子循环后,依次将栈顶元素弹出,同时将不为空的左右结点入,size--;

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res=new ArrayList<>();
        if(root==null)
        return res;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int size=queue.size();
            List<Integer> list=new ArrayList<>();
            while(size--!=0){
                TreeNode top=queue.poll();
                list.add(top.val);
               if(top.left!=null)
               queue.offer(top.left);
                if(top.right!=null)
                queue.offer(top.right);
            }
            res.add(list);
        }
        return res;
    }
}

时间复杂度;O(n)

二叉树的遍历结束

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

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

相关文章

数字医学影像系统PACS源码,三甲以下医院都能满足,C#语言开发,C/S架构系统成熟稳定,支持二次开发项目使用。

数字医学影像系统&#xff08;RIS/PACS&#xff09;源码&#xff0c;三甲以下的医院都能满足。开发技术&#xff1a;C/S架构&#xff0c;C#开发语言&#xff0c;数据库服务器采用Oracle数据库。 PACS系统模块组成 &#xff1a; 工作站&#xff1a; 分诊工作站、超声工作站、放…

二叉树详解(1)

文章目录 目录1. 树的概念及结构1.1 树的相关概念1.2 树的表示1.3 树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09; 2. 二叉树的概念及结构2.1 概念2.2 特殊的二叉树2.3 二叉树的存储结构 3. 二叉树的顺序结构及实现3.1 二叉树的顺序结构3.2 堆的概念及结构…

监控电脑屏幕被拍照的原理是什么?如此有趣的电脑防偷窥知识,你一学就会!

“防身立志言为本&#xff0c;修业安命行为先”。在数字化时代&#xff0c;保护个人隐私与信息安全已成为我们不可忽视的重要课题。 当我们坐在电脑前工作、学习或娱乐时&#xff0c;是否曾想过&#xff0c;屏幕上的内容也可能成为他人窥探的目标&#xff1f; 今天&#xff0c…

【NI-DAQmx入门】基于TSN的以太网NI CompactDAQ机箱(9185/9189)

软件兼容性&#xff1a; WIN10 64bit&#xff1a; LabVIEW 2019 32bit NI-DAQmx 19.6 NI-Sync 19.5&#xff08;TSN&#xff09; WIN11 64bit&#xff1a; LabVIEW 2022Q3 32bit NI-DAQmx 2022Q3 32bit NI-TimeSync 2022 Q4&#xff08;TSN&#xff09; 如果要启用TSN&…

力扣 | 递增子序列 | 动态规划 | 最长递增子序列、最长递增子序列的个数、及其变式

文章目录 一、300. 最长递增子序列二、673. 最长递增子序列的个数三、变式1、646. 最长数对链2、1218. 最长定差子序列3、1027. 最长等差数列4、354. 俄罗斯套娃信封问题5、1964. 找出到每个位置为止最长的有效障碍赛跑路线 四、2024复旦计科夏令营机试 最长递增子序列&#xf…

Docker容器逃逸

Docker容器逃逸 Privileged 特权模式容器逃逸 | T Wiki (teamssix.com)https://wiki.teamssix.com/cloudnative/docker/docker-privileged-escape.html 1、云原生-Docker 安全-容器逃逸&特权模式 2、云原生-Docker 安全-容器逃逸&挂载 Procfs 3、云原生-Docker 安…

Linux云计算 |【第二阶段】OPERATION-DAY3

主要内容&#xff1a; Nginx调度器&#xff08;7层代理服务器Http、Nginx&#xff0c;4层代理服务器SSH&#xff09;、配置upstream服务器集群池属性&#xff0c;HTTP错误代码&#xff0c;Nginx优化&#xff08;自定义404错误代码、状态页面显示、ab压力测试、客户端开启缓存、…

在HMI项目中,传感器扮演的角色是啥?一文告诉你。

说到HMI项目&#xff0c;就绕不开物联网&#xff0c;说到物联网就不得不说传感器&#xff0c;本文大千UI工场带你详细了解传感器的价值。 一、传感器的价值 在HMI&#xff08;Human-Machine Interface&#xff09;项目中&#xff0c;传感器扮演着收集和监测实时数据的角色。传…

Tire树-存储与查找

#include <iostream>using namespace std;const int N 100010; // 定义常量 N 表示字典树节点的最大数量int son[N][26], cnt[N], idx; // son数组存储字典树&#xff0c;cnt数组记录某个字符串结束时的节点个数&#xff0c;idx表示当前字典树的节点总数 char str[N];…

数据结构之----堆

一、介绍 堆是一棵完全二叉树。堆又分为大堆&#xff0c;小堆两种结构。 大堆&#xff1a;所有的父节点都比它的子节点要大。 小堆&#xff1a;所有的父节点都比它的子节点要小。 二、堆的向上调整算法 比如要建一个小堆 思路&#xff1a;将父节点和子节点比较&#xff0c…

驰骋BPM RunSQL_Init SQL注入漏洞复现

0x01 产品简介 驰骋BPM系统由济南驰骋信息技术有限公司研发,具有悠久的历史和丰富的行业经验。其工作流引擎CCFlow自2003年开始研发,是国内知名的老牌工作流引擎,在BPM领域拥有广泛的研究群体与应用客户群。统提供.net与java两个版本,且两个版本的代码结构、数据库结构、设…

手写数字识别实战

全部代码&#xff1a; import matplotlib.pyplot import torch from torch import nn # nn是完成神经网络相关的一些工作 from torch.nn import functional as F # functional是常用的一些函数 from torch import optim # 优化的工具包import torchvision from matplotlib …

简单回归问题实战

数据表&#xff1a;链接: https://pan.baidu.com/s/1sSz7F_yf_JeumXcP4EjE5g?pwd753f 提取码: 753f 核心流程&#xff1a; import numpy as np # 计算误差函数 points是数据集中数据的位置 def compute_error_for_line_given_points(b,w,points):totalError0for i in range(0…

【FreeRTOS】队列的本质

目录 0 前言1. 数据传输的方法1.1 任务之间如何传输数据1.2 队列的本质1.3 操作队列的三个步骤 2 队列2.1 举例说明2.2 唤醒流程2.2.1 情况12.2.2 情况2 3 总结 0 前言 学习视频&#xff1a; 【FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS&#xff08;FreeRTOS教程 基…

Haproxy基于cookie的会话保持

cookie value&#xff1a;为当前server指定cookie值&#xff0c;实现基于cookie的会话黏性&#xff0c;相对于基于 source 地址hash 调度算法对客户端的粒度更精准&#xff0c;但同时也加大了haproxy负载&#xff0c;目前此模式使用较少&#xff0c; 已经被session 共享服务器代…

亚信安慧AntDB-M聚合下推—加速你的数据分析查询

摘 要 在业务系统中&#xff0c;一般的事务型SQL语句涉及到的数据记录数不会很多&#xff0c;即便涉及到多个数据节点&#xff0c;基于AntDB-M的优化&#xff0c;访问也都很快。但是统计分析型SQL语句往往涉及到大量数据&#xff0c;甚至包括全表数据&#xff0c;基本都会覆盖…

3D 技术对我们的生活有哪些影响?

3D技术&#xff0c;也称为三维技术&#xff0c;是指利用计算机生成或处理三维数据的技术。它在多个领域对我们的生活产生了深远的影响&#xff1a; 1、制造业&#xff1a;3D技术使得个性化和定制化生产成为可能&#xff0c;大幅缩短了产品从设计到制造的时间&#xff0c;降低了…

【人工智能】Transformers之Pipeline(十):视频分类(video-classification)

目录 一、引言 二、视频分类&#xff08;video-classification&#xff09; 2.1 概述 2.2 技术原理 2.3 应用场景 2.4 pipeline参数 2.4.1 pipeline对象实例化参数 2.4.2 pipeline对象使用参数 2.4 pipeline实战 2.5 模型排名 三、总结 一、引言 pipeline&#x…

网络编程 8/15 基于UDP多人聊天室

//客户端代码 #include <myhead.h> struct msgType {char type; // 消息类型L:登录&#xff0c;Q:退出&#xff0c;C:聊天char usrName[20];char msgText[1024]; }; #define SER_PORT 6666 // 服务器端口 #define SER_IP "192.168.2.161" // 服务器IP…

SpringBoot解决创建项目无法选择JDK8和JDK11

文章目录 解决方案1解决方案2 在创建SpringBoot项目的时候&#xff0c;我们发现只能勾选JDK17以上的。并且官方没有提供2.X版本&#xff0c;但是目前大多数企业使用的还是 springboot 初始化的网址&#xff0c;我们一般使用的是官方的网址。 解决方案1 就选择jdk17和spring…