PTA天梯 L2-004 这是二叉搜索树吗?

一棵二叉搜索树可被递归地定义为具有下列性质的二叉树:对于任一结点,

  • 其左子树中所有结点的键值小于该结点的键值;
  • 其右子树中所有结点的键值大于等于该结点的键值;
  • 其左右子树都是二叉搜索树。

所谓二叉搜索树的“镜像”,即将所有结点的左右子树对换位置后所得到的树。

给定一个整数键值序列,现请你编写程序,判断这是否是对一棵二叉搜索树或其镜像进行前序遍历的结果。

输入格式:
输入的第一行给出正整数 N ( ≤ 1000 ) N(≤1000) N1000。随后一行给出 N N N 个整数键值,其间以空格分隔。

输出格式:
如果输入序列是对一棵二叉搜索树或其镜像进行前序遍历的结果,则首先在一行中输出 Y E S YES YES ,然后在下一行输出该树后序遍历的结果。数字间有 1 1 1 个空格,一行的首尾不得有多余空格。若答案是否,则输出 N O NO NO

输入样例 1:

7
8 6 5 7 10 8 11

输出样例 1:

YES
5 7 6 8 11 10 8

输入样例 2:

7
8 10 11 8 6 7 5

输出样例 2:

YES
11 8 10 7 5 6 8

输入样例 3:

7
8 6 8 5 10 9 11

输出样例 3:

NO

首先明确前序遍历和后序遍历:

  • 前序遍历: 根节点 ==>> 左子树 ==>> 右子树 (从左到右)
  • 后序遍历: 左子树 ==>> 右子树 ==>> 根节点 (从左向右)

如下图所示:


  • 前序遍历
    在这里插入图片描述

  • 后序遍历
    在这里插入图片描述

按照题目描述,给出的是正常的二叉树的前序遍历或者镜像后的二叉树的前序遍历,所以我们只需要根据给出的前序遍历序列来确定后序遍历序列就可以了,并不需要建一棵树。

我们可以使用双指针,比如命名为LeftRight,然后按照输入的序列进行搜索。
并且要区分开镜像和正常二叉树的搜索流程。

对于正常的二叉树的搜索模拟:

  • 对于Left的搜索:首先从根节点+1的地方开始搜,只要是当前所指的元素的值小于根节点的值,那么就继续往右走,则最后会指向一个小于根节点的最靠右的值(按照前序遍历顺序的)的下一个值,即根节点的右子树的根节点。
  • 对于Right的搜索:首先从最大的那一端开始搜(即前序遍历的最后一个元素),如果当前所指元素大于等于根节点,那么就继续往左扫,就相当于按照前序遍历顺序逆流,最终会指向大于等于根节点的最靠左的值(按照前序遍历顺序的)的上一个值
  • 搜到最后,如果LeftRight的数值恰好相差了1,即Left指到了右子树的根节点,Right指到了左子树的右叶节点,也就是说,我们现在确定了左子树的区间范围和右子树的区间范围
  • 那么下一步就按照以上步骤一步步递归求每一个左子树和右子树(根据后序遍历顺序,先搜左子树再搜右子树),等到在递归中搜到了树的根节点甚至都大于叶节点的时候,或者LeftRight的指向之间已经并不恰好相差1的时候,就直接结束这一层
  • 在每一层的最后都要将对应的根节点压入答案数组,因为按照这种递归顺序压入,是按照后序遍历顺序,首先压入了左子树的所有节点后,又压入了所有的右子树的所有节点,最终回归到最初的一层,也就在最后压入了根节点

而对于镜像的二叉树,就相当于正常二叉树的右子树和左子树的性质发生了调换,仅仅需要改变一下LeftRight搜索的条件就可以,注意后序遍历顺序当然还是和正常的二叉树一样先左后右。

代码:

#include<iostream>
#include<vector>
using namespace std;
const int N = 1010;

int a[N];			//读入
vector<int>ans;		//存答案
bool flag = 0;		//标记是否镜像搜索

void find(int head, int last) {	//head根节点,last前序遍历序列的最后
	if (head > last)return;		//如果根节点编号比叶节点大直接return

	int Left = head + 1;		//定义指针Left为根节点下一个点
	int Right = last;			//定义Right既为Last

	if (!flag) {	//正常二叉树搜索
		while (Left <= last && a[Left] < a[head])Left++;	//这里Left取可以等于最终元素下标,便于判断
		while (Right > head && a[Right] >= a[head])Right--;
	}
	else {			//镜像二叉树搜索
		while (Left <= last && a[Left] >= a[head])Left++;
		while (Right > head && a[Right] < a[head])Right--;
	}
	if (Left - Right != 1)return;

	find(head + 1, Right);	//搜左子树
	find(Left, last);		//搜右子树

	ans.push_back(a[head]);	//压入当前层根节点
}

int main() {
	int n; cin >> n;
	for (int i = 1; i <= n; i++)cin >> a[i];

	find(1, n);

	if (ans.size() != n) {	//如果第一次搜索出来不对,就进行镜像搜索
		flag = 1;
		ans.clear();	//清除答案数组
		find(1, n);
	}

	if (ans.size() != n)cout << "NO" << endl;
	else {
		cout << "YES\n";
		for (int i = 0; i < ans.size(); i++) {
			if (i == ans.size() - 1)cout << ans[i];
			else cout << ans[i] << " ";
		}
	}

	return 0;
}
  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
根据引用的定义,二叉搜索树具有以下性质: 1. 任一节点的左子树中所有节点的键值小于该节点的键值; 2. 任一节点的右子树中所有节点的键值大于等于该节点的键值; 3. 任一节点的左右子树都是二叉搜索树。 根据引用的描述,我们可以通过递归来构造二叉搜索树。具体来说,对于一个序列,首先确定序列的起始点作为根节点,然后从起始点的下一个节点开始遍历,直到找到第一个比根节点大或等于的节点作为右子树的起始点,而左子树的节点则是在起始点和右子树起始点之间的节点。如果在遍历过程中存在比根节点小的节点,说明该序列不是二叉搜索树。 根据引用,如果输入序列是一棵二叉搜索树或其镜像进行前序遍历的结果,那么首先在一行中输出"YES",然后在下一行输出该树的后序遍历结果。如果输入序列不是二叉搜索树,则输出"NO"。 所以,对于问题L2-004这是二叉搜索树吗?C,我们可以使用上述方法来判断输入序列是否是一棵二叉搜索树。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [天梯L2-004 这是二叉搜索树吗? (25 分)](https://blog.csdn.net/weixin_50026222/article/details/122836219)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [PTA L2-004 这是二叉搜索树吗?](https://blog.csdn.net/qq_51504747/article/details/127817010)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值