二叉树遍历题型汇总
第一类问题:根据前(后)序、中序生成树
模板(以根据后序、中序为例):
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; }
第二类问题:根据前(后)序、中序生成后(前)序 序列
模板:
根据前序、中序生成后序:
//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; }
第三类问题:根据前序、后序生成中序(多种可能)
我以前写的分析博客:二叉树 | 根据前序、后序生成中序
模板:
(以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; }
问题二:已知前序和后序,问有多少种形态的二叉树。
思路:在上文模板的基础上,在检测到有一组结点既可以当左子树,又可以当右子树时,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; }
当然还可以延伸出很多问题,比如要求输出所有这样形态的二叉树的层序或者中序。只要搞清楚问题的本质,题目再怎么变形都不在话下。
第四类问题:根据中序和层序来建树
参考博客:[二叉树建树] 复原二叉树(层序和中序)
思路:
在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; }