给定二叉树的前序和后序,判断二叉树是否唯一
给定二叉树的前序和后序,判断二叉树是否唯一
对于一棵二叉树,如果给定中序和前序后序任意一个,是可以确定这个树的形状的。例如给定前序和中序:
- 前序第一个结点肯定是根节点,后面是:
\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 号)在后序中为 index5;1 的后面一个也就是 2(2 号),在后序中 index4;
- index5>index4 并且两个位置之间没有元素,即 5-4-1=0。所以这个 2 既可能是 1 的左儿子,也可能是 1 的右儿子,这样就可以肯定树不唯一了;
- 2 的后面一个也就是 5(2 号),在后序中为 index1;
- index4>index1 并且两个位置之间有元素,即 4-1-1=2>0。所以 5 及之前的元素都属于 2 的左子树,5 右边、2 左边的都为 2 的右子树。图中也可以看到,确实是这样,没有其他的情况。这样即为局部子树唯一。
- ……
再给出唯一的情况:
-
前序:1、2、3、4、6、7、5
-
后序:2、6、7、4、5、3、1
![img]()
下面按思路走一遍:
- 1 为后序中 index7,2 为后序中 index1;
- index7>index1 并且二者之间有元素,即 7-1-1=5>0。所以 2 是 1 的左子树,6、7、4、5、3 属于 1 的右子树;
- 3 为后序中 index6;
- index1<index6,此时并不满足 2 的位置在 3 的右边,这是说明 3 并不属于 2 为根节点的树,而属于另外一颗子树中(这是因为 3 如果是属于 2 为根节点的树的话,3 应该在 2 的左边,因为后序是左 - 右 - 树,即儿子结点比根节点出现的早),如图所示;
- ……
可以看到这种情况还有一个特点:每个结点的出度是 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 分)


浙公网安备 33010602011771号