题目链接https://pintia.cn/problem-sets/994805342720868352/problems/994805353470869504
题目大意:给出一棵二叉树的前序和后序遍历,如果这棵树是唯一的,输出Yes并给出其中序遍历;否则输出No,并给出其中一种可能的中序遍历。
思路:思路其实是有的,就是递归来做。根确定后,找出左子树和右子树(的范围),对其继续递归。难点在于如何判断唯一or不唯一。看了一些解答后明白了:只要某一个结点只有左儿子or只有右儿子,那它就是不唯一的。因为这时这个儿子是挂在左边还是挂在右边无法确定。
那么就可以开始了。首先确定先序数组的范围[s1, t1]
和后序数组的范围[s2, t2]
。开始时pre[s1]==pos[t2]
,先序的开头等于后序的末尾,即根节点。
随后,假定pre[s1+1]
是属于根的左儿子,即左子树的根(就算是右子树也无所谓,之后还会做判断),用一个指针ptr
从pos[t2-1]
开始向前遍历,直到找到元素与根的左儿子pre[s1+1]
相等为止。
那么接下来从ptr
包括它自己一直向左到s2
都是左子树;从ptr
不包括它自己向右一直到t2-1
都是右子树。注意ptr
是在pos[]
数组内的指针,所以刚才讲的这个左右子树范围是在pos[]
数组内的。通过这个上下限我们可以得到左右子树的size,所以很容易可以得到左右子树在pre[]
数组内的范围。
接下来判断是否unique。前面假定了pre[s1+1]
为根的左儿子,那么如果pre[s1+1]
不是左儿子,而是右儿子,那么说明根只有右儿子,即树不unique了。这里t2-ptr-1
为右子树的size
if (t2 - ptr == 1)
isUnique = false;
可能你会问,那么【左子树的size为0的情况怎么判断?】问得好!因为我们已经先假定pre[s1+1]
为根的左儿子了,那么如果【左子树size为0】,那么实际上这棵树是不确定的,而不确定,意味着它挂在左边挂在右边都可以!所以我们的假定实际上是规定了【单个儿子都默认挂在左边】,因此非叶子结点不会出现【左子树size为0】的情况。
实际上柳神的写法和我相反,她是默认【单个儿子都默认挂在右边】。按照题意,任意一个单儿子,挂在左边还是挂在右边都行。所以理论上讲可以设置50%几率挂在左边、50%几率在右边。但看样例是默认挂在右边的,如果有些挂左有些挂右,不知道OJ会怎么判断。。。
因为要输出的是中序,因此注意遍历顺序
getInorder(s1 + 1, s1 + 1 + ptr - s2, s2, ptr);
in.push_back(pre[s1]);
getInorder(t1 - (t2 - ptr - 2), t1, ptr + 1, t2 - 1);
完整代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
using namespace std;
int N;
bool isUnique = true;
vector<int> pre, pos, in;
void getInorder(int s1, int t1, int s2, int t2) {
if (s1 == t1 || s2 == t2) {
in.push_back(pre[s1]);
return;
}
if (pre[s1] == pos[t2]) {
int ptr = t1 - 1;
while (ptr >= s2) {
if (pos[ptr] == pre[s1 + 1])
break;
ptr--;
}
if (t2 - ptr == 1)
isUnique = false;
getInorder(s1 + 1, s1 + 1 + ptr - s2, s2, ptr);
in.push_back(pre[s1]);
getInorder(t1 - (t2 - ptr - 2), t1, ptr + 1, t2 - 1);
}
}
int main() {
scanf("%d", &N);
pre.resize(N);
pos.resize(N);
for (int i = 0; i < N; i++)
scanf("%d", &pre[i]);
for (int i = 0; i < N; i++)
scanf("%d", &pos[i]);
int s1 = 0, t1 = N-1, s2 = 0, t2 = N-1;
getInorder(s1, t1, s2, t2);
if (isUnique)
printf("Yes\n");
else
printf("No\n");
printf("%d", in[0]);
for (int i = 1; i < in.size(); i++)
printf(" %d", in[i]);
printf("\n");
return 0;
}