1119 Pre- and Post-order Traversals(30分)

题目翻译:

给定一个二叉树的前序序列和后序序列,求一个中序序列,要求任意输出一个即可

题解思路:

转载自【PAT甲级题解记录】1119 Pre- and Post-order Traversals (30 分)

这类题的解题突破口都是三类遍历的特点。根结点、左孩子、右孩子分别表示为N、L、R,则

  1. 前序遍历:NLR
  2. 中序遍历:LNR
  3. 后序遍历:LRN

从这可以看出,三类遍历左右孩子的先后顺序都是一致的,区别在于什么时候访问根结点。

结合上述规则,可以得出三类遍历出来的序列的特点

  • 在前序序列(preorder)中:任取一段子序列,若该序列构成一棵树(包括树中各级子树),那根结点必定在序列之首。
  • 在中序序列(inorder)中:根结点的左侧必定是其左树,而右侧必定是其右树。
  • 在后序序列(postorder)中:任取一段子序列,若该序列构成一棵树(包括树中各级子树),那根结点必定在序列之首。

如何保证唯一确定一棵树?

        中序序列配合任何一个其他序列都可以确定一棵树,因为前序/后序可以确定当前子树的根结点,而中序可以确定当前子树的左右子树分别包含哪些结点。确定了左右子树和根结点,通过分治就可以得到所有结点在树中的位置。

前序+后序怎么确定树?(建议用两个样例画图理解)

         前序+后序只能在一部分情况下才可以唯一确定一棵树,我们观察发现,前序和后序中LR的顺序一致,但是N的位置正好相反,一个是在开头一个在结尾,这就是我们的突破口。我们先假设当前树(或子树)在preorder中的下标范围是pre_l~pre_r;在postorder中的下标范围是post_l~post_r。

        那么pre_l表示的就是这个树的根结点root,我们可以先选定preorder中下标pre_l+1的点,然后在postorder中寻找该点,假设postorder中该点的位置为pos,那么我们可以确定postorder中下标在pos之后的都在右子树(这一点需要画个图理解),这是我们可以确定的,这样就有两种情况:

  • 如果root存在左子树,那么后序序列中post_l~pos就应该是root的左子树,pos+1~post_r-1是root的右子树。
  • 但是如果root可能不存在左子树,也就是pos = post_r-1,那就糟糕了,我们压根没有了pos之后的点,那post_l~pos这一段既可以在左边,又可以在右边。这道题目里我们就可以任意取一个。

代码:

//
// Created by WU on 2023/3/1.
//
#include <bits/stdc++.h>

using namespace std;
vector<int> preorder, postorder, inorder;
int N;
bool isUnique;

void init() {
	cin >> N;
	preorder.resize(N, 0), postorder.resize(N, 0);
	for (int i = 0; i < N; ++i) cin >> preorder[i];
	for (int i = 0; i < N; ++i) cin >> postorder[i];
}

void build_tree(int pre_l, int pre_r, int post_l, int post_r) { // 虽然取名了build,但其实不用实际建树,但是思路相同
	if (pre_l == pre_r) { // 范围内只有一个点,这个点就是当前子树的根结点,没有左右子树可搜索
		inorder.push_back(preorder[pre_l]);
		return;
	}

	int pos = -1; // 理想情况下,左子树的根节点
	for (int i = post_l; i <= post_r - 1; ++i) {
		if (postorder[i] == preorder[pre_l + 1]) {
			pos = i;
			break;
		}
	}
	build_tree(pre_l + 1, pre_l + 1 + (pos - post_l), post_l, pos); // 搜索左子树 (L)
	inorder.push_back(preorder[pre_l]); // (N)
	if (pos == post_r - 1) { // 此时剩余的点可放在L可放在R,默认已经放在L了,无需再搜索R
		isUnique = false;
	}
	else {
		build_tree(pre_l + 1 + (pos - post_l) + 1, pre_r, pos + 1, post_r - 1); // 搜索右子树 (R)
	}
}

int main() {
	init();
	isUnique = true;
	build_tree(0, N - 1, 0, N - 1);
	cout << (isUnique ? "Yes" : "No") << endl;
	for (int i = 0; i < N; ++i) cout << (i == 0 ? "" : " ") << inorder[i];
	cout << endl;
	return 0;
}

坑点:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值