二叉树遍历题型汇总

第一类问题:根据前(后)序、中序生成树

模板(以根据后序、中序为例):

Node* build_tree(int ps,int pe,int is,int ie){
    if(ps>pe) return NULL;
    if(ps==pe) return new Node(in[is]);
    int i=is;
    while(i<=ie && in[i]!=post[pe]) i++;
    Node * node=new Node(in[i]);
    int dLeft=i-is;    //左侧元素数量 
    node->l=build_tree(ps,ps+dLeft-1,is,is+dLeft-1);
    node->r=build_tree(ps+dLeft,pe-1,i+1,ie);
    return node;
}

OJ实例:Tree Traversals

AC代码:

#include <stdio.h>
#include <memory.h>
#include <math.h>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <algorithm>
#include <map>

#define I scanf
#define OL puts
#define O printf
#define F(a,b,c) for(a=b;a<c;a++)
#define FF(a,b) for(a=0;a<b;a++)
#define FG(a,b) for(a=b-1;a>=0;a--)
#define LEN 1010
#define MAX (1<<30)-1
#define V vector<int>

using namespace std;

int post[LEN];
int in[LEN];

typedef struct Node{
    int d;
    struct Node* l=NULL;
    struct Node* r=NULL;
    Node(int d):d(d){
    }
};

Node* build_tree(int ps,int pe,int is,int ie){
    if(ps>pe) return NULL;
    if(ps==pe) return new Node(in[is]);
    int i=is;
    while(i<=ie && in[i]!=post[pe]) i++;
    Node * node=new Node(in[i]);
    int dLeft=i-is;    //左侧元素数量 
    node->l=build_tree(ps,ps+dLeft-1,is,is+dLeft-1);
    node->r=build_tree(ps+dLeft,pe-1,i+1,ie);
    return node;
}

int main(){
//    freopen("1020.txt","r",stdin);
    int i,n;
    I("%d",&n);
    FF(i,n) I("%d",&post[i]);
    FF(i,n) I("%d",&in[i]);
    Node* root=build_tree(0,n-1,0,n-1);
    queue<Node*> q;
    q.push(root);
    int cnt=0;
    while(!q.empty()){
        int sz=q.size();
        Node* t=q.front();
        q.pop();
        cnt++;
        O("%d",t->d);
        if(cnt!=n)O(" ");
        if(t->l)
            q.push(t->l);
        if(t->r)
            q.push(t->r);            
    }
    return 0;
}
View Code

 

第二类问题:根据前(后)序、中序生成后(前)序 序列

模板

根据前序、中序生成后序:

//caution: should using initialize code: " t=0; "
//pre in -> post
//        pre_start pre_end in_start in_end
void setPost(int ps,int pe,int is,int ie){
    if(ps>pe)return;//null
    if(ps==pe){
        post[t++]=pre[ps];
    }else{
        //find the elem is the pair of preOrder (ps)
        int i=is;
        while(in[i]!=pre[ps] && i<ie) i++;//redirect
        //left
        setPost(ps+1, ps+i-is, is, i-1);
        //right
        setPost(ps+i-is+1, pe, i+1, ie);
        //root
        post[t++]=pre[ps];
    }
}

根据后序、中序生成前序:

//caution: should using initialize code: " t=0; "
//post in -> pre
//        post_start post_end in_start in_end
void setPre(int ps,int pe,int is,int ie){
    if(ps>pe)return;//null
    if(ps==pe){
        pre[t++]=post[ps];
    }else{
        //find the elem is the pair of preOrder (ps)
        int i=is;
        while(in[i]!=post[pe] && i<ie) i++;//redirect
        //root
        pre[t++]=post[pe];
        //left
        setPre(ps, ps+i-is-1, is, i-1);
        //right
        setPre(ps+i-is, pe-1, i+1, ie);
    }
}

OJ实例:Postorder Traversal

AC代码:

#include <stdio.h>
#include <memory.h>
#include <math.h>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <algorithm>
#include <map>

#define I scanf
#define OL puts
#define O printf
#define F(a,b,c) for(a=b;a<c;a++)
#define FF(a,b) for(a=0;a<b;a++)
#define FG(a,b) for(a=b-1;a>=0;a--)
#define LEN 50001
#define MAX (1<<30)-1
#define V vector<int>

using namespace std;

int t=0;
int pre[LEN];
int post[LEN];
int in[LEN];

void setPost(int ps,int pe,int is,int ie){
    if(ps>pe) return;
    if(ps==pe){
        post[t++]=pre[ps];
        return;
    }
    int i=is;
    while(i<=ie && pre[ps]!=in[i]) i++;
    int ln=i-is;    //left_num
    setPost(ps+1,ps+ln,is,i-1);
    setPost(ps+ln+1,pe,i+1,ie);
    post[t++]=pre[ps];
}

int main(){
//    freopen("1138.txt","r",stdin);
    int i,n;
    I("%d",&n);
    FF(i,n) I("%d",&pre[i]);
    FF(i,n) I("%d",&in[i]);
    setPost(0,n-1,0,n-1) ;
    printf("%d",post[0]) ;
    return 0;
}
View Code

 

第三类问题:根据前序、后序生成中序(多种可能)

我以前写的分析博客:二叉树 | 根据前序、后序生成中序

模板

(以pat甲级1119为例,如果多个可能只输出其中一种。如果要执行其他的操作请读者修改)

void setIn(int preS,int preE,int postS,int postE){
    if(preS>preE) return;
    if(preS==preE){
        in[t++]=pre[preS];
        return;
    }
    int i=postS;
    while(i<=postE-1 && post[i]!=pre[preS+1]) i++;
    int ln=i-postS+1;    //left_num
    if(i==postE-1){        //more than one condition
        yes=0;
        //默认找到的结点都为【左结点】。(如果想设置为“右结点”,可以改变setIn递归函数的位置) 
        setIn(preS+1,preS+ln,postS,postS+ln-1);
        in[t++]=pre[preS];
        return;
    }
    setIn(preS+1,preS+ln,postS,postS+ln-1);
    in[t++]=pre[preS];
    setIn(preS+ln+1,preE,postS+ln,postE-1);
}

实际应用:

问题一:已知前序和后序,问是否有唯一的中序,并且输出其中一种中序遍历序列。

OJ链接:Pre- and Post-order Traversals

AC代码:

#include <stdio.h>
#include <memory.h>
#include <math.h>
#include <string>
#include <string.h>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <algorithm>
#include <map>


#define I scanf
#define OL puts
#define O printf
#define F(a,b,c) for(a=b;a<c;a++)
#define FF(a,b) for(a=0;a<b;a++)
#define FG(a,b) for(a=b-1;a>=0;a--)
#define LEN 3000
#define MAX 0x06FFFFFF
#define V vector<int>

using namespace std;

int t;
int post[LEN];
int in[LEN];
int pre[LEN];
bool yes=1;

void setIn(int preS,int preE,int postS,int postE){
    if(preS>preE) return;
    if(preS==preE){
        in[t++]=pre[preS];
        return;
    }
    int i=postS;
    while(i<=postE-1 && post[i]!=pre[preS+1]) i++;
    int ln=i-postS+1;    //left_num
    if(i==postE-1){        //more than one condition
        yes=0;
        //默认找到的结点都为【左结点】。(如果想设置为“右结点”,可以改变setIn递归函数的位置) 
        setIn(preS+1,preS+ln,postS,postS+ln-1);
        in[t++]=pre[preS];
        return;
    }
    setIn(preS+1,preS+ln,postS,postS+ln-1);
    in[t++]=pre[preS];
    setIn(preS+ln+1,preE,postS+ln,postE-1);
}

int main(){
//    freopen("1119_2.txt","r",stdin);
    int n,i;
    I("%d",&n);
    FF(i,n) I("%d",&pre[i]);
    FF(i,n) I("%d",&post[i]);
    setIn(0,n-1,0,n-1);
    puts(yes?"Yes":"No");
    FF(i,n){
        O("%d",in[i]);
        if(i!=n-1) O(" ");
    }
    puts("");
    return 0;
}
View Code

问题二:已知前序和后序,问有多少种形态的二叉树。

思路:在上文模板的基础上,在检测到有一组结点既可以当左子树,又可以当右子树时,cnt++(记录这样的结点出现的个数)。最后输出cnt的二次幂(假如有一个这样的结点,那就有左右两种形态。如果有两个,在控制左右形态的同时,左右又各有左右两种形态,一次类推,比图cnt=3 ,ans就等于8 ……)

OJ链接:1022: 二叉树

AC代码:

#include <stdio.h>
#include <memory.h>
#include <math.h>
#include <string>
#include <string.h>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <algorithm>
#include <map>


#define I scanf
#define OL puts
#define O printf
#define F(a,b,c) for(a=b;a<c;a++)
#define FF(a,b) for(a=0;a<b;a++)
#define FG(a,b) for(a=b-1;a>=0;a--)
#define LEN 3000
#define MAX 0x06FFFFFF
#define V vector<int>

using namespace std;

int t;
char post[LEN];
char pre[LEN];
int cnt;

void calc(int preS,int preE,int postS,int postE){
    if(preS>=preE) return;
    int i=postS;
    while(i<=postE-1 && post[i]!=pre[preS+1]) i++;
    int ln=i-postS+1;    //left_num
    if(i==postE-1) cnt++;
    calc(preS+1,preS+ln,postS,postS+ln-1);
    calc(preS+ln+1,preE,postS+ln,postE-1);
}

int pow(int n){
    int ans=1;
    while(n--){
        ans*=2;
    }
    return ans;
}

int main(){
//    freopen("1022:二叉树.txt","r",stdin);
    while(~scanf("%s%s",pre,post)){
        int n;
        n=strlen(pre);
        cnt=0;
        calc(0,n-1,0,n-1);
        O("%d\n",pow(cnt));        
    }

    return 0;
}
View Code

当然还可以延伸出很多问题,比如要求输出所有这样形态的二叉树的层序或者中序。只要搞清楚问题的本质,题目再怎么变形都不在话下。

第四类问题:根据中序和层序来建树

参考博客:[二叉树建树] 复原二叉树(层序和中序)

思路:

在main主程序段,在外循环用循环变量i对layer数组从左到右进行遍历,然后在内循环用循环变量j对in数组进行遍历。

匹配到in[j]==layer[i]时,退出循环,调用建树函数(在主程序段,一共调用n次)

在建树函数:

  如果传入的root结点是空指针,就把找到的结点赋值给这个空结点。

  如果非空,通过一个映射判断传入结点在in数组的索引是否比root在in数组的索引小。

    如果小,说明是root的左子树,递归调用建树函数。

    如果大,说明是root的右子树,递归调用建树函数。

模板

数据结构:

typedef struct Node{
    int d;
    struct Node* l; 
    struct Node* r; 
    Node(int d):d(d){
        l=r=NULL;
    }
}; 
Node * root;
map<int,int> num2index; //求一个数在中序中的索引 
int layer[LEN];
int in[LEN];

核心代码:

//主程序段调用
for(int i=1;i<=n;i++){    //循环变量 i 遍历整个 layer 数组
    j=1;
    while(j<=n && in[j]!=layer[i]) j++;    //找到in[j]==layer[i]
    num2index[in[j]]=j; //这个数在中序中的索引
    build_tree(root,j);
}
//建树函数
void build_tree(Node* & root,int i){// index
    if(!root){
        root=new Node(in[i]);
        return;
    }
    int ri=num2index[root->d];    //root_index;
    if(i<ri) build_tree(root->l,i);
    else     build_tree(root->r,i);
}

下面我们通过一个OJ例题来演示一下。

OJ链接:1010: 还原二叉树

AC代码:

#include <stdio.h>
#include <memory.h>
#include <math.h>
#include <string>
#include <string.h>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <algorithm>
#include <map>


#define I scanf
#define OL puts
#define O printf
#define F(a,b,c) for(a=b;a<c;a++)
#define FF(a,b) for(a=0;a<b;a++)
#define FG(a,b) for(a=b-1;a>=0;a--)
#define LEN 3000
#define MAX 0x06FFFFFF
#define V vector<int>

using namespace std;

map<int,int> num2index; //求一个数在中序中的索引 
int layer[LEN];
int in[LEN];
int n;

typedef struct Node{
    int d;
    struct Node* l; 
    struct Node* r; 
    Node(int d):d(d){
        l=r=NULL;
    }
}; 
Node * root;
void build_tree(Node* & root,int i){// index
    if(!root){
        root=new Node(in[i]);
        return;
    }
    int ri=num2index[root->d];    //root_index;
    if(i<ri) build_tree(root->l,i);
    else     build_tree(root->r,i);
}

vector<int> ans;

void preOrder(Node* node){
    if(node){
        ans.push_back(node->d);
        preOrder(node->l);
        preOrder(node->r);
    }
}

void postOrder(Node* node){
    if(node){
        postOrder(node->l);
        postOrder(node->r);
        ans.push_back(node->d);
    }
}

void printAns() {
    int sz=ans.size(),i;
    FF(i,sz){
        O("%d",ans[i]);
        if(i!=sz-1) O(" ");
    }
    puts("");
}

int main(){
//    freopen("还原二叉树.txt","r",stdin);
    I("%d",&n);
    int i,j;
    F(i,1,n+1) I("%d",&layer[i]);
    F(i,1,n+1) I("%d",&in[i]);
    for(int i=1;i<=n;i++){    //循环变量 i 遍历整个 layer 数组
        j=1;
        while(j<=n && in[j]!=layer[i]) j++;    //找到in[j]==layer[i]
        num2index[in[j]]=j; //这个数在中序中的索引
        build_tree(root,j);
    }
    preOrder(root);
    printAns();
    ans.clear();
    postOrder(root);
    printAns();
    return 0;
}
View Code

 

posted @ 2018-03-11 22:38  TQCAI  阅读(1123)  评论(0编辑  收藏  举报