以一个问题作为引子:
给定一棵二叉树的先序遍历序列和中序遍历序列,重建这棵二叉树。
先序序列提供树的根节点
按该节点在中序序列中的位置将序列分为左右两个子序列,可以利用左右子序列长度在先序序列中找到左右子树,递归构建这棵二叉树。
代码如下:
ps:静态建树美滋滋
//先序中序得后序
#include<iostream>
#include<string>
using namespace std;
const int maxn=1000;
struct node{
char ch;
node *rchild,*lchild;//左右孩子
}nodes[maxn];
int size=0;
node *creat(){
return &nodes[size++];
}
string preOrder,inOrder;
int len;
node *tree(int preL,int preR,int inL,int inR){//先序第一个结点,先序最后一个结点,中序第一个结点,中序最后一个结点
if(preL<=preR){//必须按照这种方法构造因为无法保证每个单独子节点恰好preL==preR
if(preL==preR)
cout<<preOrder[preL]<<endl;
node *r=creat();
r->ch=preOrder[preL];//记录结点数据
//寻找该结点在中序序列中所对应位置
int i=inL;
for(;i<=inR;i++){
if(preOrder[preL]==inOrder[i])
break;
}
//已找到该节点
int lenl=i-inL;//左边子序列长度
int lenr=inR-i;
r->lchild=tree(preL+1,preL+lenl,inL,i-1);
r->rchild=tree(preL+lenl+1,preR,i+1,inR);
return r;//右边子序列长度
}
else{
return NULL;
}
}
void postOrder(node *r){
if(r!=NULL){
postOrder(r->lchild);
postOrder(r->rchild);
cout<<r->ch;
}
}
int main(){
cin>>preOrder>>inOrder;
len=preOrder.length();
cout<<"输入串"<<endl;
cout<<"先序序列:"<<preOrder<<endl;
cout<<"中序序列:"<<inOrder<<endl;
node *root=tree(0,len-1,0,len-1);
postOrder(root);
return 0;
}
注意递归边界!!!
中序序列+先序序列
中序序列+后序序列
中序序列+层次序列
均可重构二叉树
但是其余不行,因为需要依靠中序序列提供位置。
后序序列+中序序列重构二叉树
和中序序列+先序序列没有多大区别
因为静态建树,oj无法通过。。。。
#include<iostream>
#include<queue>
using namespace std;
const int maxn=1000;
struct node{
int value;
node *lchild,*rchild;//左右子树
}nodes[maxn];
int size=-1;
node *creat(){
size++;
return &nodes[size];
}
int postOrder[maxn],inOrder[maxn];//后序序列,中序序列
int n;//序列长度
node* tree(int postL, int postR, int inL, int inR){
if(postL>postR)
return NULL;
node *r=creat();
r->value=postOrder[postR];
int k;//中序序列位置
for(k=inL;k<=inR;k++){
if(inOrder[k]==postOrder[postR])
break;
}
int lenR=inR-k;
int lenL=k-inL;
r->lchild=tree(postL,postL+lenL-1,inL,k-1);
r->rchild=tree(postL+lenL,postR-1,k+1,inR);
}
int num=0;//num数组记录空格
void layer(node *r){
queue<node*> q;
q.push(r);
while(!q.empty()){
node* tmp=q.front();
q.pop();
cout<<tmp->value;
if(tmp->lchild!=NULL){
q.push(tmp->lchild);
}
if(tmp->rchild!=NULL){
q.push(tmp->rchild);
}
num++;
if(num<n){
cout<<" ";
}
}
}
int main(){
cin>>n;
for(int i=0;i<n;i++)
cin>>postOrder[i];
for(int i=0;i<n;i++)
cin>>inOrder[i];
node *r=tree(0,n-1,0,n-1);
layer(r);
return 0;
}
中序序列+层次序列重构二叉树
刚开始觉得下标无法使用,没法写了
看了胡凡大神的代码豁然开朗,胡凡大神果然牛逼
思路如下:
1.递归建树
2.中序序列仍可以用下标标记
3.层次序列 引入vector,leftvector保存左子树结点,rightvector保存右子树结点,vector第一个元素必定是根节点,递归建树即可
4.注意判断条件为vector.size()>0
代码如下:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
const int maxn=1000;
struct node{
int value;
node *lchild,*rchild;
}nodes[maxn];
int size=-1;
node* creat(){
size++;
return &nodes[size];
}
int inorder[maxn];//中序序列
//构建树
node *tree(vector<int> layer,int inL,int inR){
if(layer.size()==0){//判断条件写不好
return NULL;
}
node *root=creat();
//层次遍历第一个结点必定根结点
int rootNum=layer[0];
root->value=rootNum;
int k;//寻找根节点所在位置
for(k=inL;k<=inR;k++){
if(inorder[k]==rootNum){
break;
}
}
//根据根节点位置,逐个遍历层次序列
//左子树结点加入左子树存储器,右子树结点加入右子树存储器
vector<int> leftTree,rightTree;
for(int i=1;i<layer.size();i++){//去掉头节点,i从1开始
int flag=false;
for(int j=inL;j<k;j++){
if(layer[i]==inorder[j]){
flag=true;
break;
}
}
if(flag){
leftTree.push_back(layer[i]);
}
else{
rightTree.push_back(layer[i]);
}
}
root->lchild=tree(leftTree,inL,k-1);
root->rchild=tree(rightTree,k+1,inR);
return root;
}
//先序遍历
void preOrder(node* root){
if(root!=NULL){
cout<<root->value<<" ";
preOrder(root->lchild);
preOrder(root->rchild);
}
}
int main(){
vector<int> layer;//层次序列
//结点个数
int n;
cin>>n;
//输入中序序列
for(int i=0;i<n;i++){
cin>>inorder[i];
}
//输入层次序列
int tmp;
for(int i=0;i<n;i++){
cin>>tmp;
layer.push_back(tmp);
}
//构建树
node *r=tree(layer,0,n-1);
preOrder(r);
return 0;
}
虽然不能重构树,但是可以重构一种可能的树序列
题目如下:
给出一棵二叉树的先序后序序列输出这棵二叉树的中序序列。
想出这种只要顺序遍历,判断是不是子节点结束的人实在是太厉害了>链接在此
//leetcode创意解法
#include<iostream>
#include<fstream>
#include<string>
#include<vector>
using namespace std;
struct node{
char ch;
node *rchild,*lchild;
};
string preOrder,inOrder,postOrder;
void inVis(node *r){
if(r!=NULL){
inVis(r->lchild);
cout<<r->ch;
inVis(r->rchild);
}
}
node* creatInorder(){
vector<node*> tmp;
node *r=new node;r->ch=preOrder[0];r->lchild=NULL;r->rchild=NULL;
tmp.push_back(r);
int postLocate=0;//判断树是否遍历完成
for(int i=1;i<preOrder.size();i++){
node *x=new node;x->ch=preOrder[i];x->lchild=NULL;x->rchild=NULL;
if(tmp.back()->lchild==NULL)
tmp.back()->lchild=x;
else if(tmp.back()->rchild==NULL)
tmp.back()->rchild=x;
tmp.push_back(x);
while(postLocate!=postOrder.length()&&!tmp.empty()&&postOrder[postLocate]==tmp.back()->ch){
postLocate++;
tmp.pop_back();
}
}
return r;
}
int main(){
fstream infile("4.in.txt");
string tmp,t;
while(infile>>t){
tmp+=t;
}
int len=tmp.length()/2;//结点个数
preOrder=tmp.substr(0,len);
postOrder=tmp.substr(len,len);
node *r=creatInorder();
inVis(r);
cout<<endl;
return 0;
}
引申出另外一种问题:
已知前序和后序遍历,求中序遍历的可能的序列数
ab ba情况左右子树位置不确定 统计这种情况 最终树的可能种类为2^n
//leetcode创意解法
#include<iostream>
#include<fstream>
#include<string>
#include<vector>
using namespace std;
string preOrder,postOrder;
int main(){
fstream infile("4.in.txt");
string tmp,t;
while(infile>>t){
tmp+=t;
}
int len=tmp.length()/2;//结点个数
preOrder=tmp.substr(0,len);
postOrder=tmp.substr(len,len);
int count=0;
for(int i=0;i<preOrder.length();i++)
for(int j=0;j<postOrder.length();j++){
if(preOrder[i]==postOrder[j]&&preOrder[i+1]==postOrder[j-1]){
count++;
}
}
int ans=1;
while(count>0){
ans*=2;
count--;
}
cout<<ans<<endl;
return 0;
}