讲透学烂二叉树(三):二叉树的遍历图解算法步骤及JS代码

article/2025/9/22 5:32:34

二叉树的遍历是指不重复地访问二叉树中所有结点,主要指非空二叉树,对于空二叉树则结束返回。

二叉树的遍历分为

  • 深度优先遍历

    • 先序遍历:根节点->左子树->右子树(根左右),有的叫:前序遍历

    • 中序遍历:左子树->根节点->右子树(左根右

    • 后序遍历:左子树->右子树->根节点(左右根

  • 广度优先遍历

    • 层次遍历

1590962-20190306170951841-1886726002.jpg

二叉树-深度的优先遍历-图解

深度优先,前、中、后遍历顺序,就是组合[根左右],移动根的位置,根左右、左根右、左右根,但是我即使代码会写了,还是搞不明白这个根左右与遍历的关系毛线头在哪里,特别是中序遍历的左根右,

博主YuXi_0520的图解,应该是我看过最易懂的。这里盗图贴一下

先序遍历(DLR)

先序遍历可以想象成,小人从树根开始绕着整棵树的外围转一圈,经过结点的顺序就是先序遍历的顺序

二叉树先序遍历流程图解

让我们来看下动画,和小人儿一起跑两遍就记住啦,记住是绕着外围跑哦   

二叉树先序列遍历-gif动画演示

    

二叉树先序遍历 小人动画演示

先序遍历结果:ABDHIEJCFKG

二叉树先序遍历-js代码实现

递归实现—二叉树先序遍历

根 - 左 - 右递归

判断根结点是否为空,为空则返回null;否则取结点的值,然后去左、右结点的值

/*** @description 前序遍历 =>1.访问根节点; 2.访问左子树; 3.访问右子树* @param node {Node} 遍历的树*/
preOrderTraverse (node = this.tree) {// 数组存储数遍历值let backs = []// 递归,function tempFunction (node) {if (node.data !== null) {// 先取根结点backs.push(node.data)// 如果存在左结点,取左节点的值node.leftChild && tempFunction(node.leftChild)// 如果存在右结点,取右结点值node.rightChild && tempFunction(node.rightChild)}}tempFunction(node)return backs
}

非递归-二叉树先序遍历

取结点的值,然后将左、右结点压如栈

preOrderTraverse2 (node = this.tree) {let backs = []if (!node) {return backs}let queue = [node]while (queue.length) {// 取出最后一个结点,对这个结点进行遍历let root = queue.pop()backs.push(root.data)// 因为queue.pop,所以先存入右结点root.rightChild && queue.push(root.rightChild)root.leftChild && queue.push(root.leftChild)}return backs
}

非递归-二叉树先序遍历

下面代码应该更容易理解

如果结点存在左结点,取值,然后存入栈中,直至没有左结点是叶子,再取右边

preOrderTraverse3 (node = this.tree) {let backs = []if (!node) {return backs}let currentNode = nodelet queue = [node]while (queue.length) {if(currentNode){backs.push(currentNode.data)queue.push(currentNode)currentNode=currentNode.leftChild}else {currentNode = queue.pop()currentNode = currentNode.rightChild}}return backs
}

中序遍历(LDR)

中序遍历可以想象成,按树画好的左右位置投影下来就可以了

二叉树-中序遍历-gif动图演示

下面看下投影的过程动画,其实就是按左右顺序写下来就行了

中序遍历结果:HDIBEJAFKCG

二叉树中序遍历-JavaScript代码实现

递归实现-二叉树中序遍历

/*** @description 中遍历 =>左右根* @param node {Node} 遍历的树*/
inOrderTraverse (node = this.tree) {// 数组存储数遍历值let backs = []// 递归,function tempFunction (node) {if (node.data !== null) {// 如果存在左结点,取左节点的值node.leftChild && tempFunction(node.leftChild)// 取根结点backs.push(node.data)// 如果存在右结点,取右结点值node.rightChild && tempFunction(node.rightChild)}}tempFunction(node)return backs
}

非递归实现-二叉树中序遍历

inOrderTraverse2(node){let backs = []if(!node){return backs}let stack = [node]let currentNode = nodewhile(stack.length){if(currentNode){stack.push(currentNode)currentNode = currentNode.leftChild}else {currentNode = stack.pop()backs.push(currentNode.data)currentNode = currentNode.rightChild}}
}

后序遍历(LRD)

后序遍历就像是剪葡萄,我们要把一串葡萄剪成一颗一颗的(从左到右,剪最底下的叶子结点)。

就是围着树的外围绕一圈,如果发现一剪刀就能剪下的葡萄(必须是一颗葡萄),就把它剪下来,组成的就是后序遍历了

跟之前先序遍历绕圈的路线是一样的(先序遍历,是遇到结点,就push到数组),但是后续遍历是:递归:先取左叶结点(没有就跳过),再取右叶子结点

二叉树-后续遍历

二叉树-后续遍历-gif图

后序遍历结果:HIDJEBKFGCA

二叉树后续遍历-JavaScript代码实现

递归实现—二叉树后续遍历

/*** @description 后序遍历 =>左根右*  1.访问左子树。(先访问左子树中的左子树,再访问左子树中的右子树)*  2.访问右子树。(先访问右子树中的左子树,再访问右子树中的右子树)*  3.访问根* @param node {Node} 遍历的树*/
postOrderTraverse (node) {// 数组存储数遍历值let backs = []// 递归,function tempFunction (node) {if (node.data !== null) {// 如果存在左结点,取左节点的值node.leftChild && tempFunction(node.leftChild)// 如果存在右结点,取右结点值node.rightChild && tempFunction(node.rightChild)// 最后取根结点backs.push(node.data)}}tempFunction(node)return backs
}

非递归实现—二叉树后续遍历

postOrderTraverse2 (node){let backs = []if(!node){return backs}let stack  = [node]while(stack.length){let currentNode = stack.pop()backs.push(currentNode.data)currentNode.leftChild&&stack.push(currentNode.leftChild)currentNode.rightChild&&stack.push(currentNode.rightChild)}return  backs
}

非递归实现2—二叉树后续遍历

postOrderTraverse2 (node) {let backs = []if (!node) {return backs}let stack = []let currentNode = nodewhile (stack.length||currentNode) {if (currentNode) {stack.push(currentNode)backs .unshift(currentNode.data)currentNode = currentNode.rightChild} else {let temp = stack.pop()currentNode = temp.leftChild}}return backs
}

非递归实现3—二叉树后续遍历

postOrderTraverse3 (node) {let backs = []if (!node) {return backs}let stack = [node]while (stack.length) {let currentNode = stack.pop()backs.unshift(currentNode.data)currentNode.leftChild && stack.push(currentNode.leftChild)currentNode.rightChild && stack.push(currentNode.rightChild)}return backs
}

非递归实现4—二叉树后续遍历

postOrderTraverse4 (node) {let backs = []if (!node) {return backs}let stack = [node]let currentNode = nodelet visitedNode = nullwhile (stack.length) {if (currentNode) {stack.push(currentNode)currentNode = currentNode.leftChild} else {currentNode = stack[stack.length - 1]if (currentNode.rightChild && currentNode.rightChild !== visitedNode) {currentNode = currentNode.rightChild} else {backs.push(currentNode.data)visitedNode = currentNodestack.pop()currentNode = null}}}return backs
}

二叉树-广度优先遍历-图解

这个只有层序遍历

层序遍历

层序遍历太简单了,就是按照一层一层的顺序,从左到右写下来就行了。

二叉树-广度优先-层序遍历

层序遍历结果:ABCDEFGHIJK

二叉序层序遍历-JavaScript代码实现

/*** @description 中遍历 =>左右根* @param node {Node} 遍历的树*/
inOrderTraverse (node = this.tree) {// 数组存储数遍历值let backs = []// 递归,function tempFunction (node) {if (node.data !== null) {// 如果存在左结点,取左节点的值node.leftChild && tempFunction(node.leftChild)// 取根结点backs.push(node.data)// 如果存在右结点,取右结点值node.rightChild && tempFunction(node.rightChild)}}tempFunction(node)return backs
}

不知道通过这种方式,有没有觉得闭着眼睛都能写出前序、中序、后序 、层序了呀,不过这只是为了大家好理解,我想出的一种形象思维,为了用代码实现,我们还需要具体了解一下前序、中序、后序遍历。

真正理解三种遍历

还记得我们先序和后序遍历时候跑的顺序么?按照这个顺序再跑一次,就是围着树的外围跑一整圈

让我们来理解一下绕着外围跑一整圈的真正含义是:遍历所有结点时,都先往左孩子走,再往右孩子走

二叉树-先序与后续遍历流程图

观察一下,你有什么发现?

有没有发现,除了根结点和空结点,其他所有结点都有三个箭头指向它。

  • 一个是从它的父节点指向它

  • 一个是从它的左孩子指向它

  • 一个是从它的右孩子指向它

一个结点有三个箭头指向它,说明每个结点都被经过了三遍。

  • 一遍是从它的父节点来的时候

  • 一遍是从它的左孩子返回时

  • 一遍是从它的右孩子返回时

其实我们在用递归算法实现二叉树的遍历的时候,不管是先序中序还是后序,程序都是按照上面那个顺序跑遍所有结点的

先序中序和后序唯一的不同就是,在经过结点的三次中,哪次访问(输出或者打印或者做其他操作)了这个结点。有点像大禹治水三过家门,他会选择一次进去。

  • 先序遍历顾名思义,就是在第一次经过这个结点的时候访问了它。就是从父节点来的这个箭头的时候,访问了它。

  • 中序遍历也和名字一样,就是在第二次经过这个结点的时候访问了它。就是从左孩子返回的这个箭头的时候,访问了它。

  • 后序遍历,就是在第三次经过这个结点的时候访问了它。就是从右孩子返回的这个箭头的时候,访问了它。

怎么样,这样有没有很好的理解?

其实不管是前序中序还是后序,在程序里跑的时候都是按照同样的顺序跑的,每个结点经过三遍,第几遍访问这个结点了,就叫什么序遍历

当我们脑子里有这个概念的时候, 再去看实现代码就很好理解了,下一篇博文我会贴出和讲解具体的实现代码。

如果递归分治来理解前、中、后遍历与根左右、左根右、左右根的关系,就是按照那个跑路图,对每个最底层的子结点[前、中、后]对应[根左右、左根右、左右根]顺序来处理。

二叉树前序、中序、后序遍历相互求法

已知二叉树的广度优先遍历-层序遍历数组,是可以完全还原二叉树结构,但是

已知二叉树前序、中序、后序中的一种排序数组,是无法求出二叉树结构的。但是知道前序、中序、后序中的中序和前序或后序数组两种也可以还原二叉树,为何可以推出二叉树的数据结构。

前序根结点在最前,后续根结点在最后,如果已知前序或者后续,则中序中根结点两边的元素分布为根结点的左边结点和右边结点元素。具体操作如下:

已知前序、中序遍历,求后序遍历

  • 前序遍历=ABGDECFH

  • 中序遍历=GBEDAFCH

构建二叉树步骤:

  1. 根据前序遍历的特点,我们知道根结点root为A;

  2. 观察中序遍历GBEDAFCH。其中root节点A的左侧GBED必然是root的左子树,右侧FCH必然是root的右子树。同时,这个也分别是左子树和右子树的中序遍历的序列;

  3. 在前序遍历遍历完根节点后,接着执行前序遍历左子树,注意,是前序遍历,什么意思?就是把左子树当成一棵独立的树,执行前序遍历,同样先访问左子树的根,第2步我们已经知道左子树是BGDE(前序遍历序列)了,由此可以得到,左子树的根是B,那么在这一步得到左子树的根是B;

  4. 从第2步得到的中序遍历的节点序列中,找到B,发现B左边只有一个G,说明B的左子树只有一个叶子节点,B的右边呢?我们可以得到B的右子树有ED,再看前序遍历的序列,发现D在前,也就是说,D是先前序遍历访问的,则得到E是D的左子树,只有一个叶子节点。到这里,我们可以得到这棵树的根结点和左子树的结构了,如下图一

  5. 接着看右子树,在第2步的时候已经知道右子树是FCH这三个节点,那么先看前序遍历的序列,先出现的是C,那么C就是右子树的根结点,看右子树的中序遍历为FCH,所以F和H就分别是它的左子树和右子树,因此,右子树的结构就出来了,如下图二

二叉树线序与中序,还原二叉树

已知中序、后序遍历,求前序遍历

  • 中序遍历:GBEDAFCH

  • 后序遍历:GEDBFHCA

同理,步骤和上面的类似,还是得先找出根结点,由后序遍历的特点,根结点root在最后,所以根结点为A,再由中序遍历可以知道左子树和右子树分别为GBED、FCH;再按照上面的步骤递归分别求出左右子树即可得解。

已知前序、后序遍历,求中序遍历

已知前序、中序或者中序、后序都可以唯一确定一棵二叉树,但是已知前序、后序是无法唯一确定一棵二叉树的,解不唯一。

关于算法相关的详细代码,查看https://github.com/zhoulujun/algorithm

参考文章:

理解二叉树的三种遍历--前序、中序、后序 +层序(简明易懂)https://blog.csdn.net/weixin_44032878/article/details/88070556

js数据结构-二叉树(二叉堆) https://segmentfault.com/a/1190000017761929 (推荐阅读-理解堆排序-堆化操作)

二叉树前序、中序、后序遍历相互求法 https://blog.csdn.net/qq_34154570/article/details/82700094

JavaScript 二叉树遍历专题:算法描述与实现 https://zhuanlan.zhihu.com/p/27307626

JS - 二叉树算法实现与遍历  (更新中...) https://www.cnblogs.com/padding1015/p/7729988.html

二叉树的遍历(前序、中序、后序、已知前中序求后序、已知中后序求前序) https://www.cnblogs.com/lanhaicode/p/10390147.html

面试BAT 却被二叉树秒杀?20 道题帮你一举拿下二叉树算法题 https://zhuanlan.zhihu.com/p/88361872

转载本站文章《讲透学烂二叉树(三):二叉树的遍历图解算法步骤及JS代码》,
请注明出处:https://www.zhoulujun.cn/html/theory/algorithm/TreeGraph/8283.html


http://chatgpt.dhexx.cn/article/nusnqq3G.shtml

相关文章

二叉树的中序遍历算法(Java三种实现方法)

文章目录 题目一、二叉树的节点定义二、三种遍历方法1.递归算法思想 2.迭代算法思想 3.Morris 中序遍历算法思想 总结 题目 给定一个二叉树的根节点 root ,返回它的 中序 遍历 一、二叉树的节点定义 public class TreeNode {int val;TreeNode left;TreeNode righ…

二叉树遍历的几种常见方法

二叉树的遍历方法 一.二叉树分类: 完全二叉树满二叉树扩充二叉树平衡二叉树 二.二叉树的四种遍历方式: 前序遍历(先根,再左,最后右)中序遍历(先左,再根,最后右&#…

二叉树的三种遍历方式

目录 1.二叉树的结构: 2.二叉树的前序遍历: 3.二叉树的中序遍历: 4.二叉树的后序遍历: 5.二叉树前、中、后序的代码实现: 前序遍历函数: 中序遍历函数: 后序遍历: 完整代码&am…

图解二叉树的三种遍历

1、二叉树的遍历 前序遍历:根结点 —> 左子树 —> 右子树 中序遍历:左子树—> 根结点 —> 右子树 后序遍历:左子树 —> 右子树 —> 根结点 层次遍历:仅仅需按层次遍历就可以 前序遍历:1 2 4 5 7…

二叉树的遍历【 详细讲解 】

二叉树的遍历 一共有4种遍历 先看图,对于这个图进行4种遍历的讲解 1、 先序遍历 定义:若二叉树为空,则空操作;否则 (1)访问根节点(2)先序遍历左子树(3&#…

二叉树的创建及遍历方法

目录 1、二叉树的定义及特点 2、满二叉树和完全二叉树的概念 3、二叉树的存储结构 4、创建二叉树 5、树的四种遍历方法 6、结果及其分析 1、二叉树的定义及特点 二叉树是指树中节点的度不大于2的有序树,它是一种最简单且最重要的树。二叉树的递归定义为&#…

二叉树遍历(图解)

二叉树的顺序存储结构就是用一维数组存储二叉树中的节点,并且节点的存储位置,也就是数组的下标要能体现节点之间的逻辑关系。—–>一般只用于完全二叉树 链式存储—–>二叉链表 定义: lchild | data | rchild(两个指针域&…

图解二叉树及二叉树遍历

二叉树及二叉树遍历 完全二叉树二叉树的遍历遍历的性质 1、完全二叉树 对于一棵具有n个节点的二叉树(按层序编号),如果编号为i的节点与同样深度的满二叉树中编号为i的节点在二叉树的位置完全相同,则为完全二叉树。 换句话来说&a…

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

目录 一、什么是二叉树? 二、二叉树的遍历 1. 先序遍历 2. 中序遍历 3.后序遍历 4. 遍历的推导 三、重要的事情 一、什么是二叉树? 1. 二叉树:一种树形结构,特点是每个结点至多只有两颗子树,并且子树有左右之分…

图解法:三分钟掌握二叉树的三种遍历

二叉树作为树中的一种特殊存在机制,人们对于它的排序总结出来了三种方式,让我们一起探寻它的魅力吧! 测试对象 1.先序遍历 首先看一下排序规则 先访问根节点 再先序访问左子树 再先序访问右子树 看上面的素材,得知根节点为…

二叉树的各种遍历算法以及实例

一、二叉树 在计算机科学中,树是一种重要的非线性数据结构,直观地看,它是数据元素(在树中称为结点)按分支关系组织起来的结构。二叉树是每个节点最多有两个子树的有序树。通常子树被称作“左子树”(left s…

二叉树的四种遍历方法(前序遍历、中序遍历、后序遍历、层序遍历)有图有真相!!!

文章目录 二叉树的四种遍历方式前序遍历(Preorder Traversal)中序遍历(Inorder Traversal)后序遍历(Postorder Traversal)层序遍历(Level Order Traversal) 二叉树的四种遍历方式 相…

二叉树的层次遍历算法

前面学的二叉树的遍历是把二叉树看作3个部分:根,左子树,右子树,然后我们以此来访问3个部分 而层次遍历是把树看成从上到下的若干层:根结点在第一层,根结点的孩子在第二层,根结点的孩子的孩子在第…

二叉树各种遍历算法

二叉树是许多算法题的常用结构,其遍历算法的各种变种就是各种题目。具体的顺序如下: 先序遍历:先根、后左、再右中序遍历:先左、后根、再右后序遍历:先左、后右、再根 递归版先序、中序、后序遍历 最简单、最直接的…

带你图解二叉树的多种递归遍历(建议收藏!!)

文章目录 二叉树的构建结点类型的定义构建二叉树之间的关系 深度优先遍历前序遍历代码实现图解递归(由于图片大小问题,建议用手机客户端查看,以下图解也是) 中序遍历代码实现图解递归 后序遍历代码实现图解递归 广度优先遍历层序遍…

二叉树遍历之图解Mirror算法(莫里斯算法)

144. 二叉树的前序遍历 我们写二叉树的遍历时,一般有两种方式,迭代和递归。然而还有一种神奇的算法,也可以作我们的二叉树递归,且空间复杂度为O(1),要知道,我们迭代和递归都是需要额外栈空间的 递归和迭代网…

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

目录 一、前序遍历 (1)递归版本 (2)非递归版本 二、中序遍历 (1)递归版本 (2)非递归版本 三、后序遍历 (1)递归版本 (2)非递归版…

详细图解二叉树四种遍历(前序中序后序层次遍历)

文章目录 一.前序遍历常规操作简单方法 二.中序遍历常规操作简单方法 三.后序遍历常规操作 四.层次遍历常规操作 本文中以此二叉树为例 一.前序遍历 常规操作 先根,再左,再右 确定了遍历整体结构: 确定了左子树中的整体结构 继续操作&…

FPN细节剖析以及pytorch代码实现

目录 FPN(feature pyramid network) 网络结构 bottleneck pytorch代码实现 公式:卷积层输入输出大小的计算公式 细节一:代码中blocks参数的含义 细节二:c1 c2 c3 c4 c5层尺寸分别为原图的1/2 1/4 1/8 1/16 1/32…

目标检测之FPN、AugFPN、NAS-FPN

针对小目标的检测有提升的文章。 未完待续~ Feature Pyramid Networks for Object Detection FPN是一种多尺度的目标检测算法。大多数目标检测算法都是采用顶层特征来做预测的,但是我们知道:低层的特征语义信息较少,但是位置信息丰富&#x…