PAT (Advanced Level) Practice 1119 Pre- and Post-order Traversals 插入法构建二叉树

一、概述

根据前序和后序遍历,判断二叉树是否唯一。

乍一看有点懵,怎么判断呢?还是要上手模拟一下。

前序遍历结构:根节点、左子树、右子树

后序遍历结构:左子树、右子树、根节点

假设两个树都不空,那么是可以唯一确定一棵树的:通过前序确定左子树的根,后序确定右子树的根,然后分别遍历,可以将两个遍历各分为三部分。

那什么时候不能确定呢?在有一颗子树为空时不能确定,即一个节点它既是左子树的根又是右子树的根。这样两个都可以,就无法确定了。知道了这一点,就可以开始着手做了。

二、分析

我发现我这次用一种很奇怪的方式去建树。又不使用引用又不使用返回值。这很蛋疼。通常的方法这两个必须至少占其一的。代码看起来也容易晕。先来看看吧:

void create(int lpre,int rpre,int lpost,int rpost,Node* root)
{
	if(lpre>=rpre||lpost>=rpost)
		return;
	/*if(lpre==rpre||lpost==rpost)
	{
		Node* leaf=new Node;
		leaf->data=lroot;
		leaf->lchild=NULL;
		leaf->rchild=NULL;
		root=leaf;
	}*/
	int lroot=pre[lpre+1];
	int rroot=post[rpost-1];
	if(lroot==rroot)
	{
		uniquetree=0;
		Node* leaf=new Node;
		leaf->data=lroot;
		leaf->lchild=NULL;
		leaf->rchild=NULL;
		root->lchild=leaf;
		create(lpre+1,rpre,lpost,rpost-1,root->lchild);//当做左根,只建左子树 
	}
	else
	{
		int lrootinpost=-1;
		for(int i=0;i<N;i++)
		{
			if(post[i]==lroot)
			{
				lrootinpost=i;
				break;	
			}
		}
		int rrootinpre=-1;
		for(int i=0;i<N;i++)
		{
			if(pre[i]==rroot)
			{
				rrootinpre=i;
				break;	
			}
		}
		int ltreenum=rrootinpre-lpre-1;
		int rtreenum=rpost-lrootinpost-1;
		Node* lleaf=new Node;
		lleaf->data=lroot;
		lleaf->lchild=NULL;
		lleaf->rchild=NULL;
		root->lchild=lleaf;
		Node* rleaf=new Node;
		rleaf->data=rroot;
		rleaf->lchild=NULL;
		rleaf->rchild=NULL;
		root->rchild=rleaf;
		create(lpre+1,rrootinpre-1,lrootinpost-ltreenum+1,lrootinpost,root->lchild);
		create(rrootinpre,rrootinpre+rtreenum-1,lrootinpost+1,rpost-1,root->rchild);
	}
}

看了代码之后我才明白为什么我不用引用了。用引用是为了不出现值传递导致根节点没法初始化,我这个是先初始化完了,在一个以存在的,弄好的节点上给他加左右子树,运行一次加两个。这样一来就不能用平时的方法了。

平时是我知道有一个空节点,我去建一个把它填上;

这个是我有一个节点,去给它加左右子树,细究起来与插入法建树有点像。

由于一次加两个节点,因此左等于右的时候也要返回,只有一个显然不够。

因此对于建树类题目,在设计建树算法时,首先要想明白,我是给一个空节点赋值,还是往实节点下面添节点。这点想明白了函数也就好写了。

在建树的时候就可以判断是否唯一了,不唯一可以默认是左子树,然后插进去继续做就行。

三、总结

又是一道建树的题目。建树的小坑可真不少。

PS:代码如下:

#include<stdio.h>
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<set>
#include<vector>
#include<math.h>
#include<algorithm>
using namespace std;
//前序遍历和后序遍历,前序遍历可得根,假设根后面的是左子树的根
//后序遍历可得根,假设根前面的是右子树的根
//如果这两个不相等,说明唯一
//如果相等,令其为左子树的根,右子树不存在
int pre[35],post[35];
struct Node
{
	int data;
	Node* lchild;
	Node* rchild;
};
int uniquetree=1;
Node* root=new Node;
int N;
void create(int lpre,int rpre,int lpost,int rpost,Node* root)
{
	if(lpre>=rpre||lpost>=rpost)
		return;
	/*if(lpre==rpre||lpost==rpost)
	{
		Node* leaf=new Node;
		leaf->data=lroot;
		leaf->lchild=NULL;
		leaf->rchild=NULL;
		root=leaf;
	}*/
	int lroot=pre[lpre+1];
	int rroot=post[rpost-1];
	if(lroot==rroot)
	{
		uniquetree=0;
		Node* leaf=new Node;
		leaf->data=lroot;
		leaf->lchild=NULL;
		leaf->rchild=NULL;
		root->lchild=leaf;
		create(lpre+1,rpre,lpost,rpost-1,root->lchild);//当做左根,只建左子树 
	}
	else
	{
		int lrootinpost=-1;
		for(int i=0;i<N;i++)
		{
			if(post[i]==lroot)
			{
				lrootinpost=i;
				break;	
			}
		}
		int rrootinpre=-1;
		for(int i=0;i<N;i++)
		{
			if(pre[i]==rroot)
			{
				rrootinpre=i;
				break;	
			}
		}
		int ltreenum=rrootinpre-lpre-1;
		int rtreenum=rpost-lrootinpost-1;
		Node* lleaf=new Node;
		lleaf->data=lroot;
		lleaf->lchild=NULL;
		lleaf->rchild=NULL;
		root->lchild=lleaf;
		Node* rleaf=new Node;
		rleaf->data=rroot;
		rleaf->lchild=NULL;
		rleaf->rchild=NULL;
		root->rchild=rleaf;
		create(lpre+1,rrootinpre-1,lrootinpost-ltreenum+1,lrootinpost,root->lchild);
		create(rrootinpre,rrootinpre+rtreenum-1,lrootinpost+1,rpost-1,root->rchild);
	}
}
int nodenum=0;
void inorder(Node* root)
{
	if(root->lchild!=NULL)
	inorder(root->lchild);
	printf("%d",root->data);
	nodenum++;
	if(nodenum<N)
	printf(" ");
	if(root->rchild!=NULL)
	inorder(root->rchild);
}
int main()
{
	scanf("%d",&N);
	for(int i=0;i<N;i++)
		scanf("%d",&pre[i]);
	for(int i=0;i<N;i++)
		scanf("%d",&post[i]);
	root->data=pre[0];
	root->lchild=NULL;
	root->rchild=NULL;
	create(0,N-1,0,N-1,root);
	if(uniquetree==1)
	{
		printf("Yes\n");
		inorder(root);
		printf("\n");
	}
	else
	{
		printf("No\n");
		inorder(root);
		printf("\n");
	}
}
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值