PAT (Advanced Level) Practice 1127 ZigZagging on a Tree 中后序建树、层序遍历变形

一、概述

给出一棵二叉树的中序和后序遍历,求出它的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);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值