给定二叉树的前序和后序,判断二叉树是否唯一?

16 篇文章 0 订阅

对于一棵二叉树,如果给定中序和前序后序任意一个,是可以确定这个树的形状的。例如给定前序和中序:

  • 前序第一个结点肯定是根节点,后面是:
    1.左子树、右子树
    2.只有左子树
    3.只有右子树
  • 对于前序中的根节点,在中序找找到位置后,他的左边有元素就有左子树,右边有元素就有右子树。

所以结合以上两点是可以唯一确定一棵二叉树的。

而如果把中序换成后序因为后序是左-右-根,无法从后序中判断前序中根节点后面的结点是左子树还是右子树,所以二叉树不一定唯一。

那么,如何判断是否唯一呢?下面给出思路:

  • 对于前序中的每一个根节点,在后序中去找这个根节点(1号)和根节点后面一个结点(2号)的位置
  • 在后序中,如果1号位置在2号位置之间右边并且二者之间有元素,说明之间的元素都属于1号的右子树,2号位置及以前的元素都属于1号的左子树,如果递归下去都是这样,就是唯一的;如果1号和2号位置之间没有元素,这时就没有办法判断它是属于左子树属于右子树,二叉树也就不唯一了。

下面对于2个具体的例子给出图示:

先给出不唯一的情况:

  • 前序:1、2、5、3、4
  • 后序:5、4、3、2、1

然后有以下三种情况(不是只有三种):

在这里插入图片描述
下面按上述思路大致走一下流程:

  1. 1(1号)在后序中为index5;1的后面一个也就是2(2号),在后序中index4;
  2. index5>index4并且两个位置之间没有元素,即5-4-1=0。所以这个2既可能是1的左儿子,也可能是1的右儿子,这样就可以肯定树不唯一了;
  3. 2的后面一个也就是5(2号),在后序中为index1;
  4. index4>index1并且两个位置之间有元素,即4-1-1=2>0。所以5及之前的元素都属于2的左子树,5右边、2左边的都为2的右子树。图中也可以看到,确实是这样,没有其他的情况。这样即为局部子树唯一。
  5. ……

再给出唯一的情况:

  1. 前序:1、2、3、4、6、7、5
  2. 后序:2、6、7、4、5、3、1
    在这里插入图片描述

下面按思路走一遍:

  1. 1为后序中index7,2为后序中index1;
  2. index7>index1并且二者之间有元素,即7-1-1=5>0。所以2是1的左子树,6、7、4、5、3属于1的右子树;
  3. 3为后序中index6;
  4. index1<index6,此时并不满足2的位置在3的右边,这是说明3并不属于2为根节点的树,而属于另外一颗子树中(这是因为3如果是属于2为根节点的树的话,3应该在2的左边,因为后序是左-右-树,即儿子结点比根节点出现的早),如图所示;
  5. ……

可以看到这种情况还有一个特点:每个结点的出度是0或者2。(可以这么理解:如果只有一个儿子结点的话,在递归判断的时候无法知道是否是左儿子还是右儿子;但是如果有两个或者没有的话,是可以判断的。)


下面给出代码:

#include <stdio.h>
#include <stdlib.h>
typedef struct node* tree;
struct node 
{
	int data;
	tree left;
	tree right;
};//树结点结构体
int n, pre[100], post[100];
int unique = 1;//用来标记二叉树是否唯一,先假设唯一
tree build(int prel, int prer, int postl, int postr)//前序的起始,后序的起始
{
	tree node = (tree)malloc(sizeof(struct node));
	node->data = pre[prel];
	node->left = node->right = NULL;
	if(prel == prer) return node;//根节点右边没有元素即没有子树就不需要判断了
	//在后序中从开始位置到结束位置,去找前序中起始结点的后面一个结点的位置
	int index = postl;
	while(index < postr && post[index] != pre[prel+1]) index++;
	if(postr-index-1 > 0)//中间有元素
	{
		int sum = index-postl+1;//左子树的大小
		node->left = build(prel+1, prel+sum, postl, index);
		node->right = build(prel+1+sum, prer, index+1, postr-1);
	}
	else//没有元素的话,无法判断是左儿子还是右二子,但可以肯定只有其中之一。可以假定为右儿子
	{
		unique = 0;
		node->right=build(prel+1, prer, postl, postr-1);
	}
	return node;
}
void inorder(tree t)
{
	if(!t) return ;
	inorder(t->left);
	printf("%d ", t->data);
	inorder(t->right);
}
int main()
{
	scanf("%d", &n);
	int i;
	for(i = 1; i <= n; i++) scanf("%d", &pre[i]);
	for(i = 1; i <= n; i++) scanf("%d", &post[i]);
	tree t = build(1, n, 1, n);
	if(unique) printf("only binary tree\n");
	else printf("not only\n");
	//如果不唯一,则输出的是上面假定的情况,即不确定局部树的儿子结点是左儿子还是右二子的时候,假定为右儿子
	inorder(t);
	return 0;
}

最后给出PAT该题的链接:1119 Pre- and Post-order Traversals (30 分)

  • 18
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
二叉树前序、中序和后序遍历是树的三种常见遍历方式。在给定的引用内容中,可以找到关于二叉树前序、中序和后序遍历的代码实现。 前序遍历是指先访问根节点,然后按照先左后右的顺序遍历左右子树。在给定的代码实现中,可以看到前序遍历的方法名为xianxu(),它会先输出当前节点的数据,然后递归地调用左子树和右子树的xianxu()方法。 中序遍历是指先遍历左子树,然后访问根节点,最后遍历右子树。在给定的代码实现中,可以看到中序遍历的方法名为zhongxu(),它会先递归地调用左子树的zhongxu()方法,然后输出当前节点的数据,最后递归地调用右子树的zhongxu()方法。 后序遍历是指先遍历左子树,然后遍历右子树,最后访问根节点。在给定的代码实现中,可以看到后序遍历的方法名为houxu(),它会先递归地调用左子树和右子树的houxu()方法,然后输出当前节点的数据。 所以,根据给定的代码实现,二叉树前序、中序和后序遍历分别对应xianxu()、zhongxu()和houxu()方法。 #### 引用[.reference_title] - *1* [二叉树的先序、中序、后序遍历超详解](https://blog.csdn.net/gqg_guan/article/details/127636880)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [二叉树的先序、中序、后序以及层次遍历](https://blog.csdn.net/qq_50931436/article/details/121407707)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值