序列重构树

以一个问题作为引子:
给定一棵二叉树的先序遍历序列和中序遍历序列,重建这棵二叉树。
先序序列提供树的根节点
按该节点在中序序列中的位置将序列分为左右两个子序列,可以利用左右子序列长度在先序序列中找到左右子树,递归构建这棵二叉树。
代码如下:
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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值