二叉树的递归与非递归遍历详解
二叉树是数组结构中非常重要的一种数据类型,很多数据结构与算法都是基于二叉树来进行扩展的,比如树,图等。而数据结构最重要的功能无非就是增删改查等操作,所以本篇文章主要来说说二叉树的查找怎么查找,完整代码可在我的github上查看。
本文中的左子节点和右子节点等同于左节点和右节点。
遍历形式
对于二叉树来说,比较重要的有三种遍历方式:
- 先序遍历:按照中间节点、左子树然后右子树的顺序遍历
- 中序遍历:按照左子树、中间节点然后右子树的顺序遍历
- 后序遍历:按照左子树、右子树然后中间节点的顺序遍历
可以看出这三种遍历的命名其实是根据中间节点的访问时机来命名的,然后这边再补充一种遍历方式–层序遍历,层序遍历就是从根节点开始,从上往下,从左往右进行遍历,按个人理解来说,层序遍历就是二叉树的广度优先搜索,先序遍历就是二叉树的深度优先搜索。
图解先、中、后序遍历
我们先来看看最容易理解的先序遍历是如何运行的,如下图:
上面说到先序遍历就是按照中间节点、左子树然后右子树的顺序遍历,所以从根节点,也就是图中的节点1,然后是节点1的左子树的根节点,也就是节点2,对于节点2来说,也是中左右的顺序遍历,所以节点2访问完以后就访问其左子树的根节点4,而4是叶子节点,没有子节点,则访问完节点4后,退回节点2访问节点2的右子树的根节点也就是5,访问完节点5后,同样因为节点5没有子节点,退回节点2,因为节点2的左右子树都已经访问过了,所以退回到节点1,访问节点1的右子树。以此类推,最后得到的访问顺序就是:1245367。
中序遍历运行过程如下图:
中序遍历是按照左子树、中间节点然后右子树的顺序遍历的,从根节点1开始,先寻找左子树根节点2,对于根节点2来说也同样需要先寻找其左子树的根节点也就是节点4。而节点4是叶子节点,无子节点,所以节点4就是这颗二叉树中序遍历的起点。也就是中序遍历的起始点是从根节点一直往左子树找到的第一个叶子节点(这句话不严谨,如果没有节点4,则是从节点2开始,但是这样更方便理解,而且这种情况下可以把节点4看成一个空节点)。那么剩下的过程和先序遍历类似,所得出的遍历顺序是:4251637。
后序遍历运行过程如下图:
后序遍历是按照左子树、右子树然后中间节点的顺序遍历,和中序遍历一样,先一直往左找到底,如果能找到叶子节点的话,就从该节点开始,如上图就是节点4。但是不同的是,如果没有节点4的话,那么就从节点5继续往左找,当然因为这个图中节点5是叶子节点,所以节点5就是起始节点(没有节点4的话)。那么上面的二叉树的后序遍历的顺序就是:4526731。
后序遍历的过程理解起来比较绕了点,需要稍微多花点时间理解理解。
递归实现先、中、后序遍历
递归实现先、中、后序遍历是很简单的,不过递归在人脑中模拟起来相当费劲,所以最好的办法就是把下面的模板记住
traverse (root) {
// 终止条件
if (!root) return
/** 先序遍历 */
// console.log(root.val)
// 遍历左子树
this.traverse(root.left)
/** 中序遍历 */
// console.log(root.val)
// 遍历右子树
this.traverse(root.right)
/** 后序遍历 */
// console.log(root.val)