浅谈二叉树的前序,中序,后序,顺序遍历
前置知识 \(:\) 前序(根左右),中序(左根右),后序(左右根) , 顺序(从上到下,从左到右)
例子: 前序(1,2,3,4,5,6) , 中序(3,2,4,1,6,5) , 后序(3,4,2,6,5,1)
\(①\) \(:\) 已知二叉树的中序和后序遍历,求其的前序遍历.
先上代码吧
//post代表后序,in代表中序.
void pre(int root,int l,int r){
if(l>r) return;
int k = l;
while(k<r&&in[k]!=post[root]) ++k;
cout<<post[root]<<" ";
pre(root-(r-k)-1,l,k-1);
pre(root-1,k+1,r);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&post[i]);
for(int i=1;i<=n;++i)
scanf("%d",&in[i]);
pre(n,1,n);
return 0;
}
\(①\) \(:\) 总的原理来将就是利用后序数组不断地找根 ; 利用中序数组将树分割,不断配合着后序数组找根.
\(②\) \(:\) while(k<r&&in[k]!=post[root]) ++k;这句在找上一次后序找出来的根在前序中的位置.
\(③\) \(:\) l,k-1代表这个根的左子树在中序的区间,同理k+1,r代表右子树(将树不断分割).
\(④\) \(:\) root-1是右子树根的位置,为什么? 因为后序遍历是左右根,根的前一位(右子树的最后一位)就是右子树的根;root-(r-k)-1是左子树根的位置,为什么? 根的位置减去右子树的长度(r-k),再左移一位就是左子树根(左子树最后一位),后序找根作用就在这里.
那么如何进行顺序遍历呢? 其实我们发现前序遍历中"根左右",先根再左右恰好是父子递进的关系,那么记录一个坐标,按照二叉树的性质将坐标由父辈传给子辈就好了(有些坐标对应的是虚点).再按坐标顺序输出就可得到顺序遍历.
struct node{
int pos,val;
};
vector<node> ans;
bool cmp(node a,node b) {return a.pos < b.pos;}
void pre(int root,int l,int r,int id){
if(l>r) return;
int k = l;
while(k<r&&in[k]!=post[root]) ++k;
ans.push_back(node{id,post[root]});
pre(root-(r-k)-1,l,k-1,id*2+1);
pre(root-1,k+1,r,id*2+2);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&post[i]);
for(int i=1;i<=n;++i)
scanf("%d",&in[i]);
pre(n,1,n,0);
sort(ans.begin(),ans.end(),cmp);
for(int i=0;i<ans.size();++i){
cout<<ans[i].val;
if(i != ans.size()-1) cout<<" ";
}
return 0;
}
\(②\) \(:\) 已知已知二叉树的中序和前序遍历,求其的后序遍历.
原理与 \(①\) 一样的,只是后序遍历要剪长度往前找根,前序遍历要加长度往后找根,而且因为左右根的顺序,所以输出放遍历之后.
void post(int root,int l,int r){
if(l > r) return;
int k = l;
while(k<r&&in[k]!=pre[root]) ++k;
post(root+1,l,k-1);
post(root+(k-l)+1,k+1,r);
cout<<pre[root]<<" ";
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&pre[i]);
for(int i=1;i<=n;++i)
scanf("%d",&in[i]);
post(1,1,n);//要注意此时开始的根是1,不是后序的n.
return 0;
}
可见中序的重要性(前序,后序求中序是不确定的.
关于建树的话,前序后序根与左右递进的关系都可以建.

浙公网安备 33010602011771号