[算法] 二叉树的 先序遍历、中序遍历、后序遍历

二叉树遍历 例如,“DBACEGF ABCDEFG”,通过序遍历知道根节点为D,根据中序遍历可知,左子树中序遍历为ABC,右子树中序遍历为EFG,然后通过序遍历得到左右子树的序遍历(BAC、EGF)和根节点D的左右节点(B、E),继续对左右子树(“BAC ABC”、“EGF EFG”)进行上述操作,直至结束。基于序序列和中序序列的节点数目相同,得到k-il=x-pl,所以x=k-il+pl,那么左子树序序列[pl+1,k-il+pl];注意:访问根,序遍历左子树,左子树为空或已遍历才可以遍历右子树。 阅读详情

本文根据清华大学邓俊辉老师课程《数据结构》总结,课程地址

遍历介绍

按照事先约定的某种规则或次序,对节点各访问一次而且仅一次。与向量和列表等线性结构一样,二叉树的这类访问也统称为遍历(traversal)。

二叉树本身并不具有天然的全局次序, 故为实现遍历,需通过在各节点与其孩子之间约定某种局部次序, 间接地定义某种全局次序。

按惯例左兄弟优先于右兄弟, 若记做节点 V ,及其左、右孩子 LR ,则下图所示,局
部访问的次序可有 V L RL V RL R V 三种选择。根据节点 V 在其中的访问次序,三种策略也相应地分别称作 先序遍历中序遍历后序遍历

这里写图片描述

可以根据节点 V 次序位置进行记忆,先序遍历中 V 位于前端,中序遍历中 V 位于中间,后序遍历中 V 位于后端。

下面说一下各个遍历的迭代式实现。

先序遍历

通过先序遍历操作后,返回结果的顺序如下图所示。 注意下图是最终返回的结果展示顺序,实现方法及流程并非如此。

先序遍历返回结果

C++ 实现代码如下:

//从当前节点出发,沿左分支不断深入,直至没有左分支的节点,沿途节点遇到后立即访问
template <typename T, typename VST> //元素类型、操作器
static void visitAlongLeftBranch(BinNodePosi(T) x, VST& visit, Stack<BinNodePosi(T)>& S) {
	while (x) {
		visit(x->data); //访问当前节点
		S.push(x->rChild); //右孩子入栈暂存(可优化:通过判断,避免空的右孩子入栈)
		x = x->lChild; //沿左分支深入一层
	}
}

template <typename T, typename VST> //元素类型、操作器
void travPre_I2(BinNodePosi(T) x, VST& visit) { //二叉树先序遍历算法(迭代版)
	Stack<BinNodePosi(T)> S; //辅助栈
	while (true) {
		visitAlongLeftBranch(x, visit, S); //从当前节点出发,逐批访问
		if (S.empty()) break; //直到栈空
		x = S.pop(); //弹出下一批的节点
	}
}

根据上面的代码,举个例子。

先序遍历实现流程

上图所示的二叉树遍历,流程描述如下:

  1. 从节点 a 出发,沿左分支不断深入,直至没有左分支的节点,沿途节点遇到后立即访问。首先 a 的右节点 c 直接进栈,然后访问左节点 b
  2. b 的右节点直接进栈,此时其为空节点,所以空节点进栈,访问 b 的左节点,也为空,直接进行下一步;
  3. 弹出栈顶空节点,再弹出 c,将 c 的右节点 f 直接进栈,并访问左节点 d
  4. d 的右节点 e 直接进栈,并访问左节点 ;
  5. d 的左节点为空。接下来弹出栈顶的 e ,并将 e 的右节点(空节点)直接进栈,访问 e 的左节点;
  6. e 的左节点为空。接下来弹出栈顶的 f ,并将 f 的右节点(空节点)直接进栈,访问 f 的左节点 g
  7. g 的右节点(空节点)直接进栈, 访问 g 的左节点;
  8. g 的左节点为空。弹出g 的右节点(空节点),再弹出 f 的右节点(空节点);
  9. 栈为空,遍历结束。(其实上述描述的每一次循环都会做一次栈是否为空的检查

中序遍历

通过中序遍历操作后,返回结果的顺序如下图所示。
同样需注意下图是最终返回的结果展示顺序,实现方法及流程并非如此。

中序遍历返回结果

C++ 实现代码如下:

template <typename T> //从当前节点出发,沿左分支不断深入,直至没有左分支的节点
static void goAlongLeftBranch(BinNodePosi(T) x, Stack<BinNodePosi(T)>& S) {
	while (x) { S.push(x); x = x->lChild; } //当前节点入栈后随即向左侧分支深入,迭代直到无左孩子
}

template <typename T, typename VST> //元素类型、操作器
void travIn_I1(BinNodePosi(T) x, VST& visit) { //二叉树中序遍历算法(迭代版)
	Stack<BinNodePosi(T)> S; //辅助栈
	while (true) {
		goAlongLeftBranch(x, S); //从当前节点出发,逐批入栈
		if (S.empty()) break; //直至所有节点处理完毕
		x = S.pop(); visit(x->data); //弹出栈顶节点并访问之
		x = x->rChild; //转向右子树
	}
}

根据上面的代码,举个例子。

中序遍历实现流程

上图所示的二叉树遍历,流程描述如下:

  1. 从节点 b 出发, b 进栈 S。沿左分支不断深入,遇到节点则入栈;
  2. 直至所有左分支节点处理完毕。(此时 S 中从上往下为 a、b);
  3. 弹出栈 S 顶节点 a 并访问之;
  4. 转向 a 右子树。到此处截止,为一个循环体操作。接下来对 a 右子树,对其重复循环体类似操作;
  5. 但这里a 右子树为空,所以继续弹出 b 。转向 b 右子树,对其进行重复循环体类似操作;
  6. 所以 f、d、c 依次入栈,c 在栈顶。弹出 c ,转向 c 右子树,重复循环体;
  7. c 右子树为空。弹出 d ,转向 d 右子树,重复循环体。
  8. e 入栈,弹出 e ,转向 c 右子树,重复循环体,c 右子树为空;
  9. 弹出 f,转向 f 右子树,重复循环体。
  10. g 入栈, g 出栈,转向 g 右子树,为空;
  11. 此时,没有新的节点入栈,栈中也没有其他节点,终止遍历操作。

后序遍历

通过后序遍历操作后,返回结果的顺序如下图所示。

后序遍历返回结果

C++ 实现代码如下:

template <typename T> //在以S栈顶节点为根的子树中,找到最高左侧可见叶节点
static void gotoHLVFL(Stack<BinNodePosi(T)>& S) { //沿途所遇节点依次入栈
	while (BinNodePosi(T) x = S.top()) //自顶而下,反复检查当前节点(即栈顶)
		if (HasLChild(*x)) { //尽可能向左
			if (HasRChild(*x)) S.push(x->rChild); //若有右孩子,优先入栈
			S.push(x->lChild); //然后才转至左孩子
		} else //实不得已
			S.push(x->rChild); //才向右
	S.pop(); //返回之前,弹出栈顶的空节点
}

template <typename T, typename VST>
void travPost_I(BinNodePosi(T) x, VST& visit) { //二叉树的后序遍历(迭代版)
	Stack<BinNodePosi(T)> S; //辅助栈
	if (x) S.push(x); //根节点入栈
	while (!S.empty()) {
		if (S.top() != x->parent) //若栈顶非当前节点之父(则必为其右兄),此时需
			gotoHLVFL(S); //在以其右兄为根之子树中,找到HLVFL(相当于递归深入其中)
		x = S.pop(); visit(x->data); //弹出栈顶(即前一节点之后继),并访问之
	}
}

根据上面的代码,举个例子。

后序遍历实力流程1

  1. 找到最高左侧可见叶节点 k,若有右子树优先入栈(此处为 j),但优先往左子树方向走(i 入栈);
  2. i 的右子树 h 入栈,i 无左子树,所以继续对右子树 h 进行操作;
  3. h 的右子树 g 入栈,方向到左子树(b 入栈);
  4. b 的右子树 a 入栈,b 无左子树。继续对 a 进行操作,a 无子节点;
  5. 到此为止,第一次入栈操作结束,此时栈中顶而下依次为 abghijk
  6. 接下来弹出栈顶元素 a ,访问之;
  7. ba 的父节点,不用进行 入栈操作。弹出栈顶元素 b ,访问之;
  8. 接下来是 g ,非 b 的父节点,执行入栈操作,按照1~5步骤说的方法,依次将 fedc 入栈;

后序遍历实力流程2

  1. 接下来判断是否需要执行入栈,并不断从栈中弹出节点,并访问之;
  2. 最后,栈为空,遍历结束。
二叉树的基本操作(序序列创建二叉树/序遍历递归算法/中序遍历递归算法/后序遍历递归算法/求二叉树深度的递归算法) 二叉树的存储结构有顺序存储和链式存储两种存储方式,这里我们采用使用频率较高的链式存储方式(二叉链表)来存储二叉树. 下面给出二叉树结点的定义. struct BiTreeNode//二叉树结点定义 { BiTreeNode* LChild;//左孩子指针域 int data; BiTreeNode* RChild;//右孩子指针域 }; ... 阅读详情

相关推荐

Java学习手册:(数据结构与算法-二叉树)已知序遍历和中序遍历,如何求后序遍历

算法思想:已知序遍历和中序遍历,如何求后序遍历?(1)确定树的根结点。树根是当前树中所有元素在序遍历中最出现的元素,即序遍历的第一个结点就是二叉树的根。(2)求解树的子树。找到根在中序遍历的位置,位置左边是二叉树的左孩子,位置右边是二叉树的右孩子,如果根结点左边或右边为空,那么该方向子树为空;如果根结点左边和右边都为空,那么根结点已经成为叶子结点。(3)对二叉树的左、右孩子分别进行步骤(1...

浩比的专栏 1057

数据结构--二叉树遍历(详细过程)

详细到啰嗦的程度,完全0基础都可完成。

m0_56283131的博客 4万+

二叉树遍历(七种方法)

本章主要通过运用递归与非递归方法分别对二叉树进行遍历 主要分序遍历、中序遍历后序遍历以及层次遍历四种情况进行讨论

AIGC 1万+

二叉树的五种遍历方式

目录 1、前序遍历 (1)递归实现前序遍历 (2)非递归实现前序遍历 2、中序遍历 (1)递归实现中序遍历 (2)非递归实现中序遍历 3、后序遍历 (1)递归实现后序遍历 (2)非递归实现后序遍历 4、层序遍历 二叉树是一种重要的数据结构,其遍历方式分为:深度遍历和广度遍历,深度遍历有前序、中序以及后序三种遍历方法,广度遍历即就是层次遍历。如下图: class TreeNode{ int val; TreeNode left; TreeNode ..

weixin_47382783的博客 1万+

数据结构|二叉树的三种遍历方式,你掌握了几种?

本期讲解二叉树的三种遍历方式:前序遍历、中序遍历后序遍历的详细讲解,图文并茂通俗易懂。

我的主页 1万+

二叉树序遍历、中序遍历后序遍历的递归算法与非递归算法

二叉树数据结构的定义: typedef struct TNode *Position; typedef Position BinTree; /* 二叉树类型 */ struct TNode{ /* 树结点定义 */ int Data; /* 结点数据 */ BinTree Left; /* 指向左子树 */ BinTree Right; /* 指向右...

弱爆了的雪饼的博客 6365

二叉树遍历序遍历,中序遍历后序遍历)递归与非递归算法

二叉树遍历算法,前序遍历,中序遍历,后续遍历算法的分析和实现(递归和非递归)

Tianzhenchuan的博客 978

算法】【二叉树模块】通过序遍历和中序遍历获取后序遍历数组(不重构二叉树

不重构二叉树的情况下通过序中序遍历获取后续遍历

qq_39760347的博客 393

二叉树序遍历、中序遍历后序遍历 递归和非递归算法

一、二叉树序遍历 (1)递归算法 // 递归序遍历 public static void recursionPreorderTraversal(TreeNode root) { if (root != null) { System.out.print(root.val + " "); recursionPreorderTraversal(root.left); recursionPreorderTraversal(root.right);

qq_43170213的博客 3499

二叉树的递归与非递归方式的序、中序、后序、层序遍历算法(Java版本)

介绍 本文提供了二叉树的构造、递归与非递归方式的遍历算法,其中递归方式较为简单。但在实际应用中,递归的遍历方式适用于递归深度不高的算法中,其一般使用非递归方式来解决。 二叉树建树 实验一:输入测试数据序列:A B # D # # C # # 构造二叉树示意图: 非递归遍历二叉树的原理 递归方式遍历二叉树较为简单,本文不做过多描述。非递归方式需要用到栈作为辅助数据结构来实现,非递归遍历二叉...

qq_25956141的博客 2108

小小c#算法题 - 11 - 二叉树的构造及序遍历、中序遍历后序遍历

在上一篇文章 小小c#算法题-10-求树的深度中,用到了树的数据结构,树型结构是一类重要的非线性数据结构,树是以分支关系定义的层次结构,是n(n>=0)个结点的有限集。但在那篇文章中,只是简单地把结点组合到了一块。 这次,我们来简单定义一棵二叉树的数据结构,并实现其序(根)遍历、中序(根)遍历后序(根)遍历算法。 下面来看一下序遍历,中序遍历...

weixin_33982670的博客 497

建立二叉树,实现二叉树序遍历、中序和后序遍历的非递归算法

序遍历:若二叉树为空,则空操作;否则访问根节点;序遍历左子树;序遍历右子树。 中序遍历:若二叉树为空,则空操作;否则中序遍历左子树;访问根节点;中序遍历右子树。 后序遍历:若二叉树为空,则空操作;否则后序遍历左子树;后序遍历右子树;访问根节点。/* * Created by Microsoft Visual Studio 2013 * @author: Teresa * @date: 20

Teresa0312的博客 4710

详解二叉树遍历

概述 二叉树遍历是一个很常见的问题。二叉树遍历方式主要有:序遍历、中序遍历后序遍历、层次遍历序、中序、后序其实指的是父节点被访问的次序。若在遍历过程中,父节点于它的子节点被访问,就是序遍历;父节点被访问的次序位于左右孩子节点之间,就是中序遍历;访问完左右孩子节点之后再访问父节点,就是后序遍历。不论是序遍历、中序遍历还是后序遍历,左右孩子节点的相对访问次序是不变的,总是访问左孩子...

supermouse的博客 1万+

一文弄懂二叉树的三种遍历方式

关注公众号:高性能架构探索。后台回复【资料】,可以免费领取 俗话说:学如逆水行舟,不进则退;心似平原走马,易放难收。这句话对程序员而言,体会更深。这行已经越来越卷了,时刻准备着,????。 二叉树,在面试中,已是必备的开胃菜。而在二叉树相关的面试题目中,遍历更是常考题目。本文将从二叉树遍历角度入手,从递归和非递归角度来分析和讲解二叉树遍历遍历 二叉树遍历是指从根节点出发,按照某种次序依次访问二叉树中的所有节点,使每个节点被且仅被访问一次。 二叉树遍历,有序遍历、中序遍历

高性能架构探索 2万+

二叉树遍历序、中序、后序、层次)

参照王道数据结构辅导书以及哔哩哔哩大学的部分视频,对二叉树遍历的重要知识点进行归纳,并总结做题技巧。

kusunoki的博客 4万+

数据结构—二叉树的4种遍历方式详解以及Java代码的完整演示

本文介绍了4种二叉树遍历方法,分别是前序、中序、后续、层序遍历,并且每种方法均提供了详尽的Java语言的代码演示,在最后还介绍了遍历结果推导的方法。

终生学习践行者 1万+

二叉树遍历方法——前、中、后序遍历(图解)

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

在夜里行走的博客 17万+

二叉树遍历

二叉树遍历.

涛涛的个人博客 1万+
上一篇: [算法] 递归方程 减而治之 分而治之
下一篇: 【CNN】理解卷积神经网络中的通道 channel
scxyz_
博客等级 码龄9年 303粉丝 61原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值