前言:
说到二叉树,我之前写过一篇博客,讲的就是二叉树的遍历,但是为什么再讲一遍呢?
当然,因为不同。这也解决了我之前关于树的一个疑惑。我当时建树的时候是前序建树,只有前序遍历的结果,并不能唯一的确定一棵二叉树,因而我采用了类似 a b null null c null null 的形式来确定化其树的形态,而一旦知道了中序遍历还有(前序遍历和后序遍历)的任何一个,就可以确定其二叉树形态,因而就只写序列就可以了,而不需要再用null。
问题:
知道一棵二叉树的前序遍历和中序遍历求其后序遍历。
输入:
ABC
BAC
FDXEAG
XDEFAG
原理:
首先前序遍历(根左右)的第一个元素肯定是根节点,那么通过寻找中序遍历(左根右)中根节点的位置,就可以把
中序序列分为左右子树,假设其长度为l和r,那么其前序遍历的序列长度可以表示为:1+l+r ,分别代表根,左子树,右子树。
同理,分别对左右子树进行同等操作,就可以确定二叉树的形态。
当然,后序遍历和中序遍历也适用。
Ok,let‘s see the code now !
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
//二叉树遍历。
//前序+中序确定一棵树。
//同理:后序+中序确定一棵树。
/*
思想:
前序和后序能确定根节点的位置,
通过查找中序遍历根节点的位置,可以确定其左右子树,
对其左右子树进行同样的操作,递归即得二叉树的形态。
*/
using namespace std;
string sPre,sIn;//前序遍历和中序遍历的字符串。
struct node{
char weight;
node* lChild;
node* rChild;
};
node* buildTree(int s1,int e1,int s2,int e2)
{
node* root=new node({sPre[s1],NULL,NULL});
int rootId;
for(int i=s2;i<=e2;i++)//寻找中序遍历root结点的位置
{
if(sIn[i]==sPre[s1])
{
rootId=i;
break;
}
}
if(rootId!=s2)//如果中序遍历的根节点前面有结点肯定是其左子树,其结点个数为rootId-s2
{
root->lChild=buildTree(s1+1,s1+rootId-s2,s2,rootId-1);
}
if(rootId!=e2)//同理,中序遍历根节点后面有结点肯定是其右子树,结点数为e2-rootId
{
root->rChild=buildTree(e1-e2+rootId+1,e1,rootId+1,e2);
}
return root;
}
void postOrder(node* root)
{
if(root->lChild!=NULL)
postOrder(root->lChild);
if(root->rChild!=NULL)
postOrder(root->rChild);
cout<<root->weight;
}
int main()
{
while(cin>>sPre>>sIn)
{
node*root=buildTree(0,sPre.length()-1,0,sIn.length()-1);
postOrder(root);
cout<<endl<<endl;
}
return 0;
}
结果:
总结:
本身题目并不难,但是解决了自己的一个疑问,很值!