一、概述
给出一棵二叉树的中序和后序遍历,求出它的z字遍历。
观察z字遍历我们可以知道,它的奇数层与层序遍历相同,偶数层与层序遍历不同。因此可以选择在层序遍历的基础上修改。
层序遍历来自BFS,使用队列。而使用队列明显无法满足输出要求。将样例写一遍可以发现其具有栈的性质。因此选择使用栈。
但是一个栈不够用,尝试使用两个栈。发现仍有错误。
使用两个栈,分别选择左右入栈和右左入栈。
得到正确结果。
因此对于样例进行上手模拟还是很有必要的。
二、分析
首先是根据中序和后序建树。
这里最开始我懵住了。到底是使用Node*还是Node建树。栈中的元素类型是Node还是Node*。都不是很清楚。
下面来看两份代码,均可使用。
其一,使用Node*作为参数,也是我最开始的代码:
void createtree1(int inl,int inr,int postl,int postr,Node* &root)
{
if(inr<inl||postr<postl)
return;
int rootinpost=postorder[postr];
int rootinorder=-1;
int i;
for(i=0;i<N;i++)
{
if(inorder[i]==rootinpost)
break;
}
rootinorder=i;
Node* leaf=new Node;
leaf->data=rootinpost;
leaf->lchild=NULL;
leaf->rchild=NULL;
root=leaf;
int ltreenum=rootinorder-inl;
int rtreenum=inr-rootinorder;
createtree1(inl,rootinorder-1,postl,postl+ltreenum-1,root->lchild);
createtree1(rootinorder+1,inr,postl+ltreenum,postl+ltreenum+rtreenum-1,root->rchild);
}
调用:
Node* root;
createtree1(0,N-1,0,N-1,root);
在开始写的时候我有一个错误,即最后一个参数使用的是Node* root。那么来分析一下。
首先我们看root的类型,是结构体指针,说明它的内容是一个地址,这个地址指向的是一个结构体。而我们传入的是root本体,也就是root的内容。这样一来,在函数中root=leaf这一步,将leaf赋值给root时出现了错误。
leaf也是一个结构体指针,它的内容也是一个地址,也就是把一个地址赋值给另一个地址。这样看来好像没错。但是,注意,这两个地址都是在内容中,也就是我犯了新手常犯的一个错误:想改变变量的值,但是却进行了值传递而不是地址传递。因此root的值在函数运行之后没有变化。
修改后最后一个变量为Node* &root,注意这是C++的一个新的特性,即引用,引用即在值传递的变量前面加一个&,从而达到地址传递的效果。这样在调用参数时root还是root。但是却实现了改变其值的效果。
其二,参数中就没有Node的事儿,但是返回值为Node*,如下:
Node* createtree(int inl,int inr,int postl,int postr)
{
if(inr<inl||postr<postl)
return NULL;
int rootinpost=postorder[postr];
int rootinorder=-1;
int i;
for(i=0;i<N;i++)
{
if(inorder[i]==rootinpost)
break;
}
rootinorder=i;
Node* leaf=new Node;
leaf->data=rootinpost;
leaf->lchild=NULL;
leaf->rchild=NULL;
int ltreenum=rootinorder-inl;
int rtreenum=inr-rootinorder;
leaf->lchild=createtree(inl,rootinorder-1,postl,postl+ltreenum-1);
leaf->rchild=createtree(rootinorder+1,inr,postl+ltreenum,postl+ltreenum+rtreenum-1);
return leaf;
}
使用这种方式时要注意在递归出口返回NULL。
调用:
root=createtree(0,N-1,0,N-1);
观察一下这种方法,是返回一个Node*。
我们分析一下,我最开始的错误是使用值传递,那现在干脆不使用传递,我新建一个节点并返回总可以吧,在main里面,你就是值传递,他也是成立的啊。
这其实是两种不同的思路:
上面的思路是有一个空的root,进到这个空的root里,选择一个值,新建一个节点,为其赋值,并令空节点等于这个新建的节点,继续往下走;
下面的思路是看你有一个空的root,我选一个值,新建一个节点,赋值,返回这个节点给你。你自己去让他相等。
前者把这个更新空节点的过程自己做完了,后者只是返回给你一个节点,你必须自己进行更新。
对于重建二叉树来说,使用后者的方法较好,更清晰易懂。
下面总结一下建树的几种情况:
一、建二叉树
一般是给出各序遍历来重建。
使用返回节点法。
二、建排序树
一般是直接插入建立。
两种方法:
一是先建好根节点,赋好值,然后在根节点上建树。
二是不建根节点,使用根节点的引用,直接更新根节点。
三、给出层序位置建树
一般使用静态树。用结构体数组来做。
接下来看创建节点的代码逻辑:
先判断左右下标是否合理,注意是右小于左而不是右小于等于左。不合理则返回。
否则对根节点进行定位,然后新建节点并复制,对于引用的,直接更新,否则返回,当然要DFS之后再返回。
最后建它的左边和右边的树即可。
建好树之后开始z字遍历。每当栈空,层数加一。根据层数选择不同的栈进行压入和弹出即可。
如下:
void ztravel(Node* root,int level)
{
Node* first=root;
s1.push(first);
while(!s1.empty()||!s2.empty())
{
if(level%2==1)
{
Node* oddNode=s1.top();
s1.pop();
if(s1.empty())
level++;
cout<<oddNode->data;
nowspace++;
if(nowspace<N)
cout<<" ";
if(oddNode->rchild!=NULL)
{
Node* rchild=oddNode->rchild;
s2.push(rchild);
}
if(oddNode->lchild!=NULL)
{
Node* lchild=oddNode->lchild;
s2.push(lchild);
}
}
else
{
Node* evenNode=s2.top();
s2.pop();
if(s2.empty())
level++;
cout<<evenNode->data;
nowspace++;
if(nowspace<N)
cout<<" ";
if(evenNode->lchild!=NULL)
{
Node* lchild=evenNode->lchild;
s1.push(lchild);
}
if(evenNode->rchild!=NULL)
{
Node* rchild=evenNode->rchild;
s1.push(rchild);
}
}
}
}
有一点要注意,栈的元素使用Node*,而不是Node。
我们要明确一下,栈中存的是什么,存的是一个个节点,那么就要使用Node*,如果使用node的话,我们就要新建一个节点,为其赋值,然后压入栈中,这样不是不可以,但是太麻烦了。
三、总结
建树这里虽然做了几道题,但是对于细节还是有一些掌握不牢,要注意理解并记忆。
PS:代码如下:
#include<stdio.h>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<map>
#include<vector>
#include<set>
#include<queue>
#include<stack>
#include<math.h>
#include<algorithm>
using namespace std;
//使用两个栈,栈1和栈2实现
//栈一保存奇数层元素,先左后右,栈二保存偶数层,先右后左
struct Node
{
int data;
Node* lchild;
Node* rchild;
};
//中后序建树
int inorder[35];
int postorder[35];
int N;
Node* root;
void createtree1(int inl,int inr,int postl,int postr,Node* &root)
{
if(inr<inl||postr<postl)
return;
int rootinpost=postorder[postr];
int rootinorder=-1;
int i;
for(i=0;i<N;i++)
{
if(inorder[i]==rootinpost)
break;
}
rootinorder=i;
Node* leaf=new Node;
leaf->data=rootinpost;
leaf->lchild=NULL;
leaf->rchild=NULL;
root=leaf;
int ltreenum=rootinorder-inl;
int rtreenum=inr-rootinorder;
createtree1(inl,rootinorder-1,postl,postl+ltreenum-1,root->lchild);
createtree1(rootinorder+1,inr,postl+ltreenum,postl+ltreenum+rtreenum-1,root->rchild);
}
Node* createtree(int inl,int inr,int postl,int postr)
{
if(inr<inl||postr<postl)
return NULL;
int rootinpost=postorder[postr];
int rootinorder=-1;
int i;
for(i=0;i<N;i++)
{
if(inorder[i]==rootinpost)
break;
}
rootinorder=i;
Node* leaf=new Node;
leaf->data=rootinpost;
leaf->lchild=NULL;
leaf->rchild=NULL;
int ltreenum=rootinorder-inl;
int rtreenum=inr-rootinorder;
leaf->lchild=createtree(inl,rootinorder-1,postl,postl+ltreenum-1);
leaf->rchild=createtree(rootinorder+1,inr,postl+ltreenum,postl+ltreenum+rtreenum-1);
return leaf;
}
stack<Node*> s1,s2;
int nowspace=0;
void ztravel(Node* root,int level)
{
Node* first=root;
s1.push(first);
while(!s1.empty()||!s2.empty())
{
if(level%2==1)
{
Node* oddNode=s1.top();
s1.pop();
if(s1.empty())
level++;
cout<<oddNode->data;
nowspace++;
if(nowspace<N)
cout<<" ";
if(oddNode->rchild!=NULL)
{
Node* rchild=oddNode->rchild;
s2.push(rchild);
}
if(oddNode->lchild!=NULL)
{
Node* lchild=oddNode->lchild;
s2.push(lchild);
}
}
else
{
Node* evenNode=s2.top();
s2.pop();
if(s2.empty())
level++;
cout<<evenNode->data;
nowspace++;
if(nowspace<N)
cout<<" ";
if(evenNode->lchild!=NULL)
{
Node* lchild=evenNode->lchild;
s1.push(lchild);
}
if(evenNode->rchild!=NULL)
{
Node* rchild=evenNode->rchild;
s1.push(rchild);
}
}
}
}
int main()
{
scanf("%d",&N);
for(int i=0;i<N;i++)
{
scanf("%d",&inorder[i]);
}
for(int j=0;j<N;j++)
{
scanf("%d",&postorder[j]);
}
createtree1(0,N-1,0,N-1,root);
//root=createtree(0,N-1,0,N-1);
ztravel(root,1);
}