先来一段不痛不痒的文:
先复习一下二叉树的先、中、后序遍历的访问顺序(在节点存在的情况下):
1、先序遍历(VLR) 根节点-> 左孩子-> 右孩子
2、中序遍历(LVR) 左孩子-> 根节点-> 右孩子
3、后序遍历(LRV) 左孩子-> 右孩子-> 根节点
假设现在有
一组先序遍历输出的字符序列const char *VLR = "ABCDEFGH";
一组中序遍历输出的字符序列const char *LVR = "CBEDFAGH";
一组后序遍历输出的字符序列const char *LRV = "CEFDBHGA";
根据三种遍历的输出特点,我们可以肯定,VLR中的第一个字符A一定是一个根节点,且是整颗二叉树的根节点,再根据中序序列先左再根后右的特点可以知道,在LVR中A左边的序列CBEDF,这5个字符都属于A的左子树,A右边的2个字符构成的序列GH一定属于A的右子树,将对创建左子树有用的序列CBEDF称创建左子树时的作有效序列,对创建右子树的序列GH也称作创建右子树时的有效序列
再来看VLR中的第二个输出B,但从VLR中看,我们不能断言B就是A的左孩子,因为存在A的左孩子不存在且B是A的右孩子的可能,这种可能下,B在A之后紧挨着A输出,但是我们可以结合LVR来看(VLR不能结合LRV来看,按照和之前同样的推法你可以发现这两种序列下的节点关系并不唯一),VLR中的第二个输出B,在LVR序列中位于A的左边,所以B应该是A的左子树,目前有关系如下:
在看VLR中第三个C,在LVR中位于A的左边B的左边,所以C是B与A的共同的左子树,但二叉树中节点的最大度为2,所以得出关系如下:
第三个D,在LVR中位于A的左边,C与B的右边,可得出关系如下:
第四个E,在LVR中位于A的左边,C与B的右边,但是在D的左边,可以得出关系如下:
其余的输出也是按照同样的方法去处理,LVR与LRV也是一样的道理
//LinkBinTreeRestore.h
#ifndef _LINKBINTREERESTORE_H_
#define _LINKBINTREERESTORE_H_
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef char Element;
typedef struct _BinTreeNode
{
Element data;
struct _BinTreeNode *leftChild;
struct _BinTreeNode *rightChild;
}BinTreeNode;
typedef BinTreeNode *BinTree;
void InitBinTree(BinTree *tree);
//根据前、后序遍历结果无法唯一确定二叉树,故只有前中序、中后序创建
void CreateBinTree_1(BinTree *tree, const char *VLR, const char *LVR, int n);
void CreateBinTree_2(BinTree *tree, const char *LVR, const char *LRV, int n);
bool BinTreeCmp(BinTree tree1, BinTree tree2);
void DestroyBinTree(BinTree *tree);
#endif //_LINKBINTREERESTORE_H_
//LinkBinTreeRestore.c
#include "LinkBinTreeRestore.h"
void InitBinTree(BinTree *tree)
{
*tree = NULL;
}
/*
* 以先序遍历输出的字符作为每个节点数据域的填值依据,以中序遍历输出的字符间的位置信息作为每个节点创建的位置依据
*
*参数VLR是先序遍历输出结果字符串,LVR是中序遍历输出字符串,n为该次创建字符串的有效长度,如在创建A的左子树时,LVR中A的左边的字符序列CBEDF有效,n值为5
*/
void CreateBinTree_1(BinTree *tree, const char *VLR, const char *LVR, int n)
{
if( 0 == n )
{
*tree = NULL;
return ;
}
int i = 0;
while (VLR[0] != LVR[i]) //寻找先序遍历的输出在中序遍历中的位置,以分割左右子树
i++;
*tree = (BinTreeNode *)malloc(sizeof(BinTreeNode)); //创建根节点
if( NULL == *tree )
return ;
(*tree)->data = VLR[0];
/*以创建根节点A的左右子树为例(有效序列也就是LVR中根的左边或右边的序列):
*创建左子树时,以VLR序列中的第二个输出B(开始位置VLR + 1)开始的序列创建A的左子树,该序列的BCDEF属于有效部分,对于创建左子树LVR序列
* 的CBEDF部分(开始位置LVR)都是有效的,i用于控制有效部分长度,控制前两个参数有效部分的长度,创建A的左子树时为5
*/
CreateBinTree_1(&(*tree)->leftChild, VLR + 1, LVR, i); //创建左子树
//创建右子树时,VLR序列中的有效序列为GH为有效部分(开始位置VLR + i + 1),LVR序列有效部分GH(开始位置LVR + i + 1),创建A的右子树时有效长为2
CreateBinTree_1(&(*tree)->rightChild, VLR + i + 1, LVR + i + 1, n - i - 1); //创建右子树
}
void CreateBinTree_2(BinTree *tree, const char *LVR, const char *LRV, int n)
{
if( 0 == n )
{
*tree = NULL;
return;
}
int i = 0;
while (LRV[n - 1] != LVR[i])
i++;
*tree = (BinTreeNode *)malloc(sizeof(BinTreeNode));
if( NULL == *tree )
return ;
(*tree)->data = LRV[n - 1];
/*以创建根节点A的左右子树为例(有效序列也就是LVR中根的左边或右边的序列):
* 创建左子树时,LVR序列的CBEDF为有效序列(起始位置LVR),LRV中CEFDB为有效序列(起始位置LRV),有效长度i在创建A的左子树时为5
*/
CreateBinTree_2(&(*tree)->leftChild, LVR, LRV, i);
/*/创建右子树时,LVR序列的有效部分为GH(起始位置为LVR + i + 1),LRV中有效序列为HG(起始位置LRV + i),有效长度在创建A的右子树时为2
*/
CreateBinTree_2(&(*tree)->rightChild, LVR + i + 1, LRV + i, n - i - 1);
}
bool BinTreeCmp(BinTreeNode *node1, BinTreeNode *node2)
{
if( NULL == node1 && NULL == node2 ) //同为空则相等
return true;
if( NULL != node1 && NULL != node2 && node1->data == node2->data ) //都不为空且节点数据域值相等,判断其子树是否相等
return BinTreeCmp(node1->leftChild, node2->leftChild) && BinTreeCmp(node1->rightChild, node2->rightChild);
return false; //其余情况不等
}
void DestroyBinTree(BinTree *tree)
{
if( NULL == *tree )
return;
DestroyBinTree(&(*tree)->leftChild);
DestroyBinTree(&(*tree)->rightChild);
free(*tree);
*tree = NULL;
}
//main.c
#include <stdio.h>
#include <string.h>
#include "LinkBinTreeRestore.h"
int main(int argc, char *argv[])
{
const char *VLR = "ABCDEFGH";
const char *LVR = "CBEDFAGH";
const char *LRV = "CEFDBHGA";
BinTree tree1;
BinTree tree2;
InitBinTree(&tree1);
InitBinTree(&tree2);
CreateBinTree_1(&tree1, VLR, LVR, strlen(VLR));
CreateBinTree_2(&tree2, LVR, LRV, strlen(LVR));
BinTreeCmp(tree1, tree2) ? printf("树相同\n") : printf("树不同\n");
DestroyBinTree(&tree1);
DestroyBinTree(&tree2);
return 0;
}