树-树的遍历(先序、中序、后序)

2023-11-11

树的遍历

树的遍历方式主要分为四种,先序、中序、后序和层序,在这篇博客中我将仔细介绍一下树的这四种遍历方式。
在这里插入图片描述

先序遍历

先序遍历,也叫先根遍历、前序遍历,首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树,如果二叉树为空则返回。可以简记为根左右。
以上图为例,整体的遍历过程为:

  • 先遍历A节点
  • 然后遍历A的左子节点B节点
  • 接着遍历B节点的左子节点D节点
  • D节点没有左子节点和右子节点,然后我们回溯到B节点,遍历B节点的右子节点E节点
  • 同理E没有左子节点和右子节点,我们继续回溯到B节点,发现B节点的左右子节点都已被访问,再接着回溯到B的父节点A。
  • 遍历A节点的右节点C
  • 遍历C的左子节点F,F没有左右子节点,回溯到C节点,C节点没有右子树,回溯到A节点,此使所有的节点都已被访问完毕。
  • 遍历结果为:ABDECF

代码实现

树先序遍历的实现方式主要包括两种,递归和迭代,递归算法调用隐式栈,而迭代算法显式的调用栈。

递归实现
//根左右,先访问根节点,然后访问左子树,最后再访问右子树
public List<Integer> preorderTraversal(TreeNode root){
        List<Integer> res = new ArrayList<>();
        preorder(root,res);
        return res;
    }

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

先序遍历的迭代实现要借用数据结构栈,首先我们要知道栈这种数据结构是先进后出,所以我们入栈的时候要先压入最右的节点,然后依次从右往左压入节点,这样就能保证出栈的时候首先访问左节点。以上图为例整体的遍历流程如下:

  • 初始化一个列表res用来存放访问结果
  • 首先将根节点A压入栈中,此时栈中只有元素A,stack(A)
  • 我们将A节点出栈,存入到res中,将A的子节点依次从右到左压入栈中,此时栈顶元素为B,栈中的元素为stack(B,C)
  • 然后B节点出栈,存入res中,将B节点的子节点依次从右到左压入栈中,此使栈顶元素为D,栈中的元素为stack(D,E,C)
  • 然后我们将D节点出栈存入到res中,因为D节点为叶节点没有子节点,所以此时的栈顶元素为E,栈中元素为stack(E,C)
  • 重复上述过程,直到栈空位置,遍历结束,返回res列表,即为访问结果
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> res=new ArrayList<Integer>();
        if(root==null)
            return res;
        Stack<TreeNode> stack=new Stack<TreeNode>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode tree=stack.pop();      //先往栈中压入右节点,再压左节点,这样出栈就是先左节点后右节点了。
            if(tree.right!=null)
                stack.push(tree.right);
            if(tree.left!=null)
                stack.push(tree.left);
            res.add(tree.val);
        }
        return res;
    }

中序遍历

中序遍历,也叫中根遍历,遍历方式可以简记为左根右。首先访问左子树,然后访问根节点,最后遍历右子树,若二叉树为空结束访问,否则:

  • 中序遍历左子树
  • 访问根节点
  • 中序遍历右子树
    具体流程如下:
  • 初始化res列表存储访问结果
  • 首先访问根节点A的左子树BDE
  • 发现B也有左右子树,那么我们先访问B的左子树D
  • D没有左子树,所以我们先将D存入res列表中
  • 根据左根右的思想,然后我们访问D节点的根节点B,将B存入res列表中
  • 然后DB都访问过了,即左根都被访问了,接着我们访问B节点的右子树E
  • 因为E没有左子树,所以将E存入res列表中,判断发现E也没有右子树,我们回溯到B节点,发现B的左右节点都已被访问,我们回溯到A节点,将A节点存入列表中
  • 整棵树的左子树和根节点均已被访问,接着访问根节点A的右子树。
  • 重复上述过程,直到访问完所以的节点。

上图的访问结果为:DBEAFC

代码实现

递归实现
public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        inorder(root,res);
        return res;

    }

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

中序遍历的迭代实现要依靠栈数据结构,有一个口诀就是中序遍历不忘“左链入栈”

public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        if(root == null) return res;
        /*退出循环的条件,root != null没有特殊含义,只是让循环得以进行下去,
        循环的终止条件只要是判断栈空不空所决定*/
        while (root != null || !stack.isEmpty())
        {
            while (root != null)    //左链入栈
            {
                stack.push(root);
                root = root.left;
            }
            TreeNode node = stack.pop();  //左子树为空将根节点出栈
            res.add(node.val);
            root = node.right;   //访问右子树

        }
        return res;

    }

后序遍历

后序遍历(LRD),也叫做后根遍历,可记做左右根。后序遍历有递归算法和非递归算法两种。在二叉树中,先左后右再根,即首先遍历左子树,然后遍历右子树,最后访问根结点。

  • 后序遍历左子树

  • 后序遍历右子树

  • 访问根结点
    以上图为例我们走一遍整体的遍历流程:

  • 初始化res列表存储遍历结果

  • 首先访问根节点A的左子树BDE

  • 发现节点B也有左右子树,那我们访问B节点的左子树D

  • D节点没有左右子树,所以将D节点存入到res列表中

  • 接着我们回溯到B节点,发现其存在右子树E,那我们先访问E节点

  • E节点没有左右子树,我们将E节点存入res列表中

  • 此使B节点的左右子树访问完毕,我们将B节点存入res中,此时节点A的左子树访问完毕,我们访问其右子树。

  • 右子树仍然按照左右跟的思想进行遍历。

  • 最后在访问根节点A,至此遍历结束

代码实现

代码实现主要包括两种:

  • 递归实现
  • 迭代实现
递归实现
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);           //访问根节点
    }

迭代实现
public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        if (root == null) {
            return res;
        }

        Deque<TreeNode> stack = new LinkedList<TreeNode>();
        TreeNode prev = null;
        while (root != null || !stack.isEmpty()) {
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            //pre用来标记上一次右子树是否已经访问过
            if (root.right == null || root.right == prev) {
                res.add(root.val);
                prev = root;
                root = null;
            } else {
                stack.push(root);
                root = root.right;
            }
        }
        return res;


    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

树-树的遍历(先序、中序、后序) 的相关文章

随机推荐

  • 映射表

    集是一个集合 它可以快速地寻找现有的元素 但是要查看元素 就需要查看的元素的精确副本 这不是一种非常通用的查找方式 通常 我们知道某些键的信息 并想要查找与之相对应的元素 映射表 map 数据结构就是为此设计的 映射表用来存放键值对 如果提
  • 【Quant】80+面试,5个offer,Quant大神总结分享各家quant面试题

    Why Quant 在北美 这个工作基本上是理工科的中国学生进入金融领域最主要的渠道 而且 现在国内也有朝这方面发展的趋势 如果你是理工科背景 或者对数理 编程和金融比较感兴趣 不妨尝试在就业的时候向这个领域发展 它的一个好处是在工作的时候
  • linux中关闭防火墙

    systemctl status firewalld查看当前防火墙状态 systemctl stop firewalld关闭当前防火墙 systemctl disable firewalld开机防火墙不启动
  • 解决合并单元格筛选时只出现首行的小技巧

    前言 Excel小窍门 让办公更便捷 情景 合并单元格后 单一筛选时只会显示对应的第一行数据 原因 Excel筛选单元格时 遇到不连续区域 即中间有空白单元格 会识别不到后续内容 合并单元格后 除首行外 其余行的值会被自动清空 从而导致在筛
  • NGINX--初步变量详解

    一 编译echo模块 1 echo模块下载以及解压 wget c https gitee com mirrors echo nginx module repository archive master zip unzip master zi
  • 电脑提示d3dcompiler_47.dll缺失怎么修复?

    d3dcompiler 47 dll是 Microsoft 的 DirectX 11 核心组件之一 它主要用于编译和运行 Direct3D 11 应用程序和游戏 如果您的系统中缺少这个 DLL 文件 可能会导致一些程序无法正常运行 很多游戏
  • CMake----if与option使用小记

    在CMake中if语法比较简单 if后面括号中的参数随着CMake版本的推进 在else和endif中也可以不用写了 if address else endif 对于if语法 比较常用的就是字符串比较了 这里个人简单用到过两种 一种是这个变
  • 使用scoped穿透方法实现修改vue中mint UI组件样式

    效果 代码 div class goods swiper div
  • 图书商城系统

    摘 要 随着信息科学技术的不断发展与完善 信息化已经成为个人之间甚至是国家之间商务发展的一大趋势 并且广泛应用于商业贸易 国际化的网络 计算机科学以及网络通信之中 电子商务正是依托信息化技术的迅猛发展将全球化的市场集中在网络平台之中 打破了
  • c++基本类型和变量

    基本类型 c 内置类型 setlocale LC ALL chs bool bo true char ch a wchar t wch L 中国 short sh 32767 32768 32767 int i 10 32768 32767
  • runas 显示740 所需的操作需要提升的解决方法

    域环境中 有些软件启动需要用到管理员权限 所以对user用户来说比较麻烦 对IT来说也挺麻烦 每次使用都需要输一次账号密码 后来使用了runas工具就方便了 虽然有些不安全 今天发现这个不起作用了 cmd里输入语句 提示 740 所需的操作
  • C语言内存四区的学习总结(一)---- 静态区

    最近重新学习C语言相关知识 重新提到内存四区的概念 那么在之前的学习的基础上 在这儿做一个简单的总结与分享 一 内存四区建立的流程 可以简单直观的查看下面的这个图片 直接的说明我们的程序在内存中是如何去存储 运行 程序运行的流程说明 1 操
  • 引入字体包

    我接触的设计师都比较喜欢用苹方字体 然后每次都要引入字体包 首先一定要设计师给ttf格式的文件 然后在scss中引入 font face font family PingFangSC Regular font weight normal s
  • pandas数据读取与清洗视频03-pd.read_csv()读取csv、txt文件

    本系列课程适用人群 python零基础数据分析的朋友 在校学生 职场中经常要处理各种数据表格 或大量数据 十万级以上 的朋友 喜欢图表可视化的朋友 系列视频目前可在B站观看 会定期更新 欢迎大家吐槽 本节概要 数据量较大时一般保存为csv或
  • Wireshark TS

    问题背景 用户反馈说观察到一个设备连接的奇怪问题 客户端 172 18 0 122 尝试连接到服务器 172 18 50 1 之后服务器回复 SYN ACK 再收到消息后不久 客户端直接发送 RST 并在一段时间后又重复尝试连接 总结下来就
  • RPC通信功能实现

    Table of Contents RPC通信功能实现 配置参数 调用方法 RPC通信功能实现 HBase的RPC通信功能主要基于Protobuf和NIO这两个组件来实现 在通信管道上选择的是protobuf对外声明的BlockingRpc
  • Linux——僵尸进程以及僵尸进程的处理

    僵尸进程 1 进程中的指令已经执行完成 但是进程PCB结构还没有回收 即子进程先于父进程退出后 子进程的PCB需要其父进程释放 但是父进程并没有释放子进程的PCB 这样的子进程就称为僵尸进程 2 父进程未结束 子进程结束 但父进程没有处理子
  • C语言三大标准C89,C99和C11

    C89 标准 1983 年美国国家标准局 American National Standards Institute 简称 ANSI 成立了一个委员会 专门来制定C语言标准 1989 年C语言标准被批准 被称为 ANSI X3 159 19
  • 59 KVM Skylark虚拟机混部-概述、架构及特性

    文章目录 59 KVM Skylark虚拟机混部 概述 架构及特性 59 1 Skylark概述 59 1 1 问题背景 59 1 2 总体介绍 59 2 架构及特性 59 2 1 总体实现框架 59 2 2 功耗干扰控制 59 2 3 L
  • 树-树的遍历(先序、中序、后序)

    树的遍历 树的遍历方式主要分为四种 先序 中序 后序和层序 在这篇博客中我将仔细介绍一下树的这四种遍历方式 先序遍历 先序遍历 也叫先根遍历 前序遍历 首先访问根结点然后遍历左子树 最后遍历右子树 在遍历左 右子树时 仍然先访问根结点 然后