一.通过带空结点的先序遍历结果还原二叉树(递归思想)
这是一棵普通的二叉树,它的先序遍历的结果是:ABDCEF
要想通过这个结果来还原元一棵二叉树是办不到的,因为我们不知道左子树何时到尽头。
但是有了带空结点的遍历结果,它的先序遍历就成了这样:ABD###CE##F,当碰到“#”的时候就是遇到了空结点,就表示该创建右子树了。
分析:有了这个结果该怎样创建树呢。
还是将二叉树拆开看:
1.先建根:根据先序遍历的特点,第一个元素就是根节点
2.再建左子树,此时先序遍历的结果就要往后挪动了,同时记录下左子树消耗掉的结点的个数
3.再建右子树,先序遍历结果中根节点与左子树后面的就是右子树了,此时
用来建右子树的结点的数目=全部节点数目-左子树用掉的-根节点
来看看代码:
//用带空结点的遍历结果来创建一棵二叉树
//例:先序遍历:ABD###CE##F
//根据这个就可以创建出一棵唯一的二叉树
//函数传参,第一个先将先序遍历的结果传进去,
//再传入可以使用的结点的数目,
//最后传入已经使用的结点的数目
TreeNode* creatPreTree(char* pre, int size,int* used) {
//边界判断
if (size == 0) {
//没有可使用的节点了
*used = 0;
return NULL;
}
if (pre[0] == '#') {
//如果读到空结点的标志
*used = 1;
return NULL;
}
//先创建根节点
TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));
root->value = pre[0];
//再创建左子树
int leftused;
root->left = creatPreTree(pre + 1, size - 1, &leftused);
//再创建右子树
int rightused;
root->right = creatPreTree(pre + 1 + leftused, size - 1 - leftused, &rightused);
//更新已经使用的结点的数目值
*used = 1 + leftused + rightused;
return root;
}
二.先序+中序遍历还原二叉树
只是用一种遍历结果是无法还原处出二叉树的,那么两种遍历结果组合起来,我们就可以还原出一棵二叉树了
先考虑先序+中序
分析:
1.先建根,先序遍历的首元素就是根节点。
2.建左子树,找到根节点在中序遍历结果中的下标来确定建立左子树需要的结点数目。
3.建右子树,建完左子树后,剩下的结点就用来建右子树。
看代码:
//通过先序和中序来还原二叉树
//在中序中找根节点,中序中根节点的下标就是左子树结点的个数
//由此递归处理,直到处理完毕所有节点
Tree* recoverTree(char pre[],char mid[],int size) {
//可用的结点数目为0
if (size == 0) {
return NULL;
}
//根节点的值,在中序遍历中找到根节点的下标
char rootValue = pre[0];
int leftsize = findValue(mid,size, rootValue);
//创建根节点
Tree* root = (Tree*)malloc(sizeof(Tree));
root->value = rootValue;
//创建左子树
root->left = recoverTree(pre + 1, //左子树先序遍历
mid, //左子树的中序遍历
leftsize //左子树的可用元素个数
);
//创建右子树
root->right = recoverTree(pre + leftsize + 1, //右子树的先序遍历
mid + leftsize + 1, //右子树的中序遍历
size - 1 - leftsize //右子树的结点个数
);
return root;
}
三.中序+后序还原二叉树
分析:递归思想!!!
1.先建根,通过后序遍历得到根结点
2.建左子树,在中序遍历中找到根节点后,根节点对应的下标就是要构建的左子树的结点个数
3.建右子树,左子树建完,剩下的结点用来建右子树。
四.先序+后序还原二叉树
想啥呢。不可能!!!
因为先序与后序都只能找到根节点的位置不能划分出左右子树。
比如:
总结:
对二叉树进行操作还是那几句老话
1.想到递归
2.不要把二叉树想成一整棵树,拆开看——左子树、右子树又是二叉树
3.还原二叉树:
①可以通过带空结点的遍历结果来还原二叉树
②通过先序+中序 、或者中序+后序来还原二叉树。