原题目链接:
P1229 遍历问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
原题目截图:
思路分析:
1、为什么会出现多个不同序列?
前序遍历和后序遍历序列无法唯一确定一棵二叉树的原因主要在于这两种遍历方式都缺少了关于树结构的某些关键信息。具体来说:
前序遍历:访问顺序是“根-左-右”,即首先访问根节点,然后是左子树,最后是右子树。
后序遍历:访问顺序是“左-右-根”,即首先访问左子树,然后是右子树,最后是根节点。
这两种遍历方式都有一个共同的问题:它们都没有明确指出左子树和右子树的边界。这意味着,给定一个前序遍历序列和一个后序遍历序列,可以有多种不同的二叉树结构对应于这两个序列。
2、什么情况下,不同的二叉树,它们有相同的前序遍历和后序遍历?
看下图的例子:
通过画图举例,我们可以很容易发现:
(1)这些不同的二叉树的层次遍历结果一定是相同的
(2)如果一个节点有两个儿子,那么这一层级之间结构不可变
(3)如果一个节点只有一个儿子,那么它可以通过改变这个儿子的左右属性,来变化二叉树结构
综上可以得到一个结论:只要知道有多少对“1对1”的节点(即只有一个儿子的节点)个数,就能够知道有多少不同二叉数。
假如“1对1节点个数”为x,那么结果输出2^x。
3、如何知道“1对1节点个数”?
我们还是以上面的举例来看:
前序:abced
后序:ecdba
可以发现,对于“ab”,我们只要确认后序序列是否有“ba”,就可以判断出:a---b 一定是一组“一对一”连接。
相信说到这里,这道题就很容易了。
4、具体实现思路:我这里使用了哈希表优化了时间复杂度。
-
首先,我们读取前序遍历和后序遍历的序列。
-
然后,我们使用一个哈希表(
unordered_map
)来存储后序遍历中每个节点的位置。 -
接着,我们遍历前序遍历序列中的每个节点:
-
对于每个节点,我们找到它在后序遍历中的位置。
-
我们检查这个节点在后序遍历中左边是否有子节点(即
index - 1 >= 0
)。 -
如果有,我们再检查这个子节点是否与前序遍历中下一个节点相同(即
post[index - 1] == pre[i + 1]
)。 -
如果相同,这意味着我们不能通过这两个序列唯一确定二叉树,因此我们将结果
res
乘以2。
-
-
最后,我们输出结果。如果结果是1,表示可以唯一确定二叉树;如果结果是2,表示不能唯一确定。
解决代码:
#include<iostream>
using namespace std;
#include<unordered_map>
int main() {
string pre, post; // 定义前序遍历和后序遍历的字符串
cin >> pre; // 输入前序遍历序列
cin >> post; // 输入后序遍历序列
unordered_map<char, int> ump; // 使用哈希表存储后序遍历中每个节点的位置
for (int i = 0; i < post.size(); i++) {
ump[post[i]] = i; // 将后序遍历中的节点和其索引存入哈希表
}
int res = 1; // 初始化结果为1,表示可以唯一确定二叉树
for (int i = 0; i < pre.size(); i++) {
int index = ump[pre[i]]; // 获取前序遍历中当前节点在后序遍历中的索引
// 检查当前节点在后序遍历中左边是否有子节点,并且该子节点是否与前序遍历中下一个节点相同
if (index - 1 >= 0 && i + 1 < pre.size() && post[index - 1] == pre[i + 1]) {
res *= 2; // 如果相同,说明不能唯一确定二叉树,结果乘以2
}
}
cout << res; // 输出结果
}