二叉树的遍历
- 前序遍历
- 访问根节点
- 按前序遍历左子树
- 按前序访问右子树
- 中序遍历
- 按中序遍历左子树
- 访问根节点
- 按中序访问右子树
- 后序遍历
- 按后序访问左子树
- 按后序访问右子树
- 访问根节点
- 层序遍历
- 按层数的先后,从左到右进行遍历
不难看出,访问根节点的顺序不同是前三种遍历方式的区别所在
双遍历序列构造二叉树
单遍历序列
-
显然,仅由二叉树按一种遍历方式所得到的遍历序列是不能重新构造出原来的二叉树。如图,不难看出,满足由二叉树的一种遍历序列的二叉树结构并不唯一
不同遍历序列提供的信息
- 前序遍历
- 由于首先访问的是根节点,所以前序序列的第一个元素是当前二叉树的根节点,提供了根节点信息
- 根节点访问后,进行前序遍历左、右子树。所以,首元素后的元素可以视作当前子树的“根节点”(遍历)
- 中序遍历
- 中序遍历中对根节点的访问介于中序遍历左子树和中序遍历右子树之间,所以根节点将左右子树区分开,以根节点为界,左边是左子树的结点集合,右边是右子树的节点集合。即有,中序遍历序列可以提供二叉树的左右节点信息
- 后序遍历
- 与前序遍历类似,后序遍历最后访问根节点,所以后序遍历序列的最后一个元素是当前二叉树的根节点,提供了根节点信息
- 层序遍历
- 只能提供二叉树节点的先后顺序(从上到下、从左到右),序列首元素即为二叉树根节点,同样提供二叉树根节点信息
- 单遍历序列之所以不能还原出二叉树,因为如果是只提供了前、后和层序遍历序列,序列只能提供根节点信息,而并不能区分左右子树。同理若只提供中序遍历序列就只能提供左右子树的信息,并不能获知根节点的位置,所以不能还原
双遍历序列还原二叉树的组合
- 前序+中序
- 后序+中序
- 层序+中序
- 以上组合体现出不同信息的组合:(前序、后序和层序)提供根节点信息,然后中序序列中找到对应的根节点,即可得到左右子树信息,从而实现二叉树的还原
具体例子
前序+中序
递归法
-
对于任意一颗树而言,前序遍历的形式总是
$[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]$ 即根节点总是前序遍历中的第一个节点。而中序遍历的形式总是
$[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]$
只要我们在中序遍历中定位到根节点,那么我们就可以分别知道左子树和右子树中的节点数目。由于同一颗子树的前序遍历和中序遍历的长度显然是相同的,因此我们就可以对应到前序遍历的结果中,对上述形式中的所有左右括号进行定位。
这样以来,我们就知道了左子树的前序遍历和中序遍历结果,以及右子树的前序遍历和中序遍历结果,我们就可以递归地对构造出左子树和右子树,再将这两颗子树接到根节点的左右位置
详情见leetcode题解:力扣
-
给定一棵二叉树的前序遍历序列和中序遍历序列可以构造出这棵二叉树。例如前序序列是ABDECFG,中序序列是DBEAFCG
实现 ConstructTree.cpp 里的TNode* InPreToTree(char *pa, char *ia, int p1, int p2, int i1, int i2)函数。
该函数的功能是由前序遍历序列和中序遍历序列构造二叉树。前序序列为pa[p1:p2],中序序列为ia[i1:i2],返回所构造的二叉树的根指针。
提示1:这是一个递归函数,在主程序中调用: InPreToTree(pa,ia,0,n-1,0,n-1),其中n是序列长度。 提示2:由于在DeleteTree()中是使用delete删除一个树结点,所以在InPreToTree()需要使用new来申请结点空间。
提示3:本题所给序列各元素皆不相同
/// #include <stdio.h> #include <stdlib.h> #include <string.h> #include "ConstructTree.h" / /* InPreToTree(): 由前序遍历序列和中序遍历序列构造二叉树 前序序列为pa[p1:p2] 中序序列为ia[i1:i2] 返回所构造的二叉树的根指针 */ TNode* InPreToTree(char *pa, char *ia, int p1, int p2, int i1, int i2) { /*请在BEGIN和END之间实现你的代码*/ /*****BEGIN*****/ TNode *root = new TNode; //先序第一个即为根 root->data = pa[p1]; root->left = NULL; root->right = NULL; //如果只有一个元素,直接返回 if (p1 == p2) return root; //在中序中找到根 int i = i1; while (ia[i] != root->data && i <= i2) i++; if (i == i2 && ia[i] != root->data) exit(0); //找不到根节点,错误以退出 int leftlen = i - i1;//左子树长度 int rightlen = i2 - i;//右子树长度 if (leftlen > 0) root->left = InPreToTree(pa, ia, p1+1, p1+leftlen, i1, i-1); if (rightlen > 0) root->right = InPreToTree(pa, ia, p2-rightlen+1, p2, i+1, i2); return root; /******END******/ /*请不要修改[BEGIN,END]区域外的代码*/ } void PrintPostTravel(TNode* t) { if(t==NULL) return; if(t->left) PrintPostTravel(t->left); if(t->right) PrintPostTravel(t->right); printf("%c", t->data); } void DeleteTree(TNode* t) { if(t==NULL) return; if(t->left) DeleteTree(t->left); if(t->right) DeleteTree(t->right); delete t; }
本题基本思想是在先序遍历序列pa数组中确定根节点,再在中序遍历序列ia数组中遍历找到根节点的位置,因为中序遍历序列中根节点将左右子树节点区分在根节点的左右两边,所以就可以分别确定左右子树的节点集合。如此递归,直到左或右子树只有一个节点时返回结果
迭代法
- 详情见leetcode题解:力扣
后序+中序
- 见leetcode:力扣