L2-004 这是二叉搜索树吗? (25 分)(二叉树的遍历和二叉搜索树的综合应用)

一、类似题目和相关博客

类似题目:
L2-006 树的遍历 (25 分)
L2-011 玩转二叉树 (25 分)

相关博客:
L2-006 树的遍历 (25 分)(二叉树的遍历:根据遍历结果构造二叉树)

二、题目

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

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

所谓二叉搜索树的“镜像”,即将所有结点的左右子树对换位置后所得到的树。
给定一个整数键值序列,现请你编写程序,判断这是否是对一棵二叉搜索树或其镜像进行前序遍历的结果。

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

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

输入样例 17
8 6 5 7 10 8 11
结尾无空行
输出样例 1:
YES
5 7 6 8 11 10 8
结尾无空行
输入样例 27
8 10 11 8 6 7 5
结尾无空行
输出样例 2:
YES
11 8 10 7 5 6 8
结尾无空行
输入样例 37
8 6 8 5 10 9 11
结尾无空行
输出样例 3:
NO
结尾无空行

三、解题思路

我的思路是这样的:
可以先根据输入序列的前两个值的大小关系来判定这个是不是镜像树的前序遍历序列,如果第二个值大于等于第一个值,那么则是镜像树的前序遍历序列,否则不是,然后分为下面三种情况,对应三个输入案例:

  • 如果输入的不是对镜像进行前序遍历的序列,并且这个输入是正确的,比如8 6 5 7 10 8 11,那么就可以根据值的大小构造成一颗二叉排序树,因为是前序序列,所以8一定是根节点, 6 5 7 的值均小于8,所以是8的左子树,而10 8 11的值均大于或等于8,所以是8的右子树。然后看6 5 7,6一定是根节点, 5 的值小于6,所以是6的左子树,而7的值大于6,所以是6的右子树。同理10 8 11中,10一定是根节点, 8的值小于10,所以是10的左子树,而11的值大于10,所以是10的右子树。这样一颗二叉搜索树就构造好啦。
  • 如果输入的是对镜像进行前序遍历的序列,并且这个输入是正确的,比如8 10 11 8 6 7 5,与上面的道理是相同的,镜像只不过是把节点的左右子树换了位置,这样对于一个节点来说,其左子树中所有结点的键值大于等于该结点的键值,其右子树中所有结点的键值小于该结点的键值,其左右子树都是二叉搜索树。因为是前序序列,所以8一定是根节点,10 11 8 的值均大于或等于8,所以是8的左子树,而6 7 5 的值均小于8,所以是8的右子树。然后看10 11 8,10一定是根节点, 11的值大于10,所以是10的左子树,而8的值小于10,所以是10的右子树。同理6 7 5中,6一定是根节点, 7的值大于6,所以是6的左子树,而5的值小于6,所以是6的右子树。这样一颗二叉搜索树的镜像树就构造好啦。
  • 如果输入的序列是错误的,比如8 6 8 5 10 9 11,首先6的值小于8而8的值等于8,那么按理说6是左子树,第二个8以及它后面的元素是右子树,但是在8 5 10 9 11中又出现了比8小的值,所以与逻辑不符合,这样的输入序列就是错误的。

由上面的分析也可以看出,需要用到递归,不断缩小区间来判断,只要有一个区间不满足,那么这个输入就是错的。

四、关于map.size()的困惑以及解题代码(23分)

4.1 关于map.size()的困惑

第一次提交的结果显示测试点3,4,5没有通过,在调试过程中发现map容器一个奇怪的现象,比如在函数外面定义一个map容器:map<int,int> t,容器的默认值为0,如果使用t.size()函数,直接让一个变量k等于t.size()与for循环输出中的t.size()值是不一样的,我用下面这段代码进行了说明,书上说size()用来获取map中映射的对数,如果没有映射,那for循环中为什么还会有输出呢?有没有大佬知道这是咋回事呢??哭了。

#include <bits/stdc++.h>
using namespace std;
int main()
{
	map<int,int> t;
	t[1]=2;
    t[4]=3;
	int k=t.size();
	cout<<"k= "<<k<<endl;
	for(int i=0;i<k;i++)
	{
		cout<<t[i]<<endl;
	}
	cout<<"-------------------------"<<endl;
	for(int i=0;i<t.size();i++)
	{
		cout<<t[i]<<endl;
	}
	k=t.size();
	cout<<"k= "<<k<<endl;
	return 0;
}

运行结果:
在这里插入图片描述

4.2 解题代码(23分)

下面这个代码只得了23分,测试点5报告段错误,有大佬看到了希望能指点指点。虽然没有获得满分,代码也有些长,但花了我好几个小时,是靠我自己做出来的,还是很有成就感的,。

#include <bits/stdc++.h>
using namespace std;
int a[1002];
int k1=0;//用来记录最右下角结点的序号 
map<int,int> t;
bool isbst(int begin,int end,int index)//不是镜像时,判断是不是二叉搜索树(BST) 
{
	//begin表示子区间的开始,end表示结尾,index表示当前结点在二叉搜索树中的序号 
	if(begin>end) 
	{
		return true;
	}
	int root=a[begin];
	t[index]=root;
	k1=max(k1,index);
	if(begin==end) return true;
	int k=0;
	int f=0;
	for(int i=begin+1;i<=end;i++)
	{
		if(a[i]>=root&& f==0)
		{
			k=i;f=1;
		}
		if(a[i]<root&& f==1)
		{
			return false;
		}
	}
	if(f==0) k=end+1;
	bool flag1=isbst(begin+1,k-1,index*2+1);
	if(flag1==false) return false;
	bool flag2=isbst(k,end,index*2+2);
	if(flag2==false) return false;
	return true;
}

bool isbst1(int begin,int end,int index)//是镜像时,判断是不是二叉搜索树(BST) 
{
	if(begin>end) return true;
	int root=a[begin];
	t[index]=root;
	k1=max(k1,index);
	if(begin==end) return true;
	int k=0;
	int f=0;
	for(int i=begin+1;i<=end;i++)
	{
		if(a[i]<root&& f==0)
		{
			k=i;f=1;
		}
		if(a[i]>=root&& f==1)
		{
			return false;
		}
	}
	if(f==0) k=end+1;
	bool flag1=isbst1(begin+1,k-1,index*2+1);
	if(flag1==false) return false;
	bool flag2=isbst1(k,end,index*2+2);
	if(flag2==false) return false;
	return true;
}
//先将节点按照根右左的顺序压入栈中,然后反向输出就成为了后序遍历的顺序了。 
vector<int> ve;
void VEC(int i)
{
	if( i<=k1 && t[i]!=0)
	{
		ve.push_back(t[i]);
		VEC(2*i+2);
		VEC(2*i+1);
	}
}
int main()
{
	int n;
	cin>>n;
	for(int i=0;i<n;i++) cin>>a[i];
	bool fl;
	if(a[1]<a[0]) fl=isbst(0,n-1,0);
	else fl=isbst1(0,n-1,0);
	if(fl==true) 
	{
		cout<<"YES"<<endl;
		VEC(0);
		cout<<ve[ve.size()-1];
		for(int i=ve.size()-2;i>=0;i--)
		{
			cout<<" "<<ve[i];
		}
	}
	else  cout<<"NO"<<endl;
	return 0;
 } 
 

五、有关二叉搜索树的知识总结(待续。。。)

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值