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

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

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

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

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

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

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

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

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

先给出不唯一的情况:

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

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

img

下面按上述思路大致走一下流程:

  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

    img

下面按思路走一遍:

  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 分)

posted @ 2020-03-22 17:34  别再闹了  阅读(1201)  评论(0)    收藏  举报