(L3-010)是否完全二叉搜索树(建树+bfs)

题目链接:PTA | 程序设计类实验辅助教学平台

分析:这道题主要有三个步骤:建树->输出层序遍历->判断是否为完全二叉树

先来说一下建树,建树这个比较简单,就是我们要进行插入一个新节点,那么每次就从根节点遍历,如果该节点还没有被建立那就把值放入当前节点,如果该节点已经被建立,那么就判断待插入节点与当前遍历节点的值的关系,如果待插入节点值小于当前节点值,这时候有两种情况,一种是当前节点有右子树,那么就递归进入右子树进行后续遍历,如果当前节点没有右子树,那么就直接给当前节点建一个右子树再进行后续遍历,同理,如果待插入节点值大于当前节点值,这时候也有两种情况,一种是当前节点有左子树,那么就递归进入左子树进行后续遍历,如果当前节点没有左子树,那么就直接给当前节点建一个左子树再进行后续遍历,具体建树代码如下:

void insert(int pos,int val)//建立二叉树 
{
	if(!vis[pos])//没有建立过点就新建一个点 
	{
		t[pos]=val;
		vis[pos]=true;
		return ;
	}
	if(val>t[pos])//左子树键值大 
	{
		if(l[pos])//存在左子树就直接遍历左子树 
			insert(l[pos],val);
		else//不存在左子树就先新建节点再遍历 
			l[pos]=++idx,insert(l[pos],val);
	}
	else
	{
		if(r[pos])//存在右子树就直接遍历右子树
			insert(r[pos],val);
		else//不存在右子树就先新建节点再遍历
			r[pos]=++idx,insert(r[pos],val);
	}
}

下面来说一下层序遍历,这是用队列实现的,这个也比较容易实现,我们首先把树根放入队列中,我们每次弹出队首元素,然后进行输出,再依次把当前节点的左子树和右子树入队即可

最后来说一下如何判断一棵树是不是完全二叉树 ,这个主要是用二叉树的特点来判断的

假设二叉树高度是h,那么h-1层二叉树应该是满节点的,而且第h层二叉树的节点应该是从左到右连续的,那么我们就可以利用这个特点来进行完全二叉树判定

我们用flag标记该树是否为完全二叉树,flag1标记该树是否存在缺节点的情况

然后我们按照层序遍历对树进行遍历:

(1)如果发现有一个节点有右子树但是没有左子树,那么就将flag直接置为false

(2)如果发现之前存在节点缺孩子的情况(flag1=true),但是现在又遍历到一个节点存在孩子,那么这样的树一定不是完全二叉树(完全二叉树最后一层节点是从左到右依次相邻的)

(3)如果发现有节点缺孩子,那么就将flag1置为true

需要注意的是一定要先对(2)进行判断再对(3)进行判断,因为如果先对(3)进行判断,这个时候如果只存在左孩子而不存在右孩子会先将flag1置为true,这样再对(2)进行判断,发现该节点有孩子,所以会错误地判定该树不是完全二叉树,这样判断是不对的,相信大家能够发现这一点

最后附上本题代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=1e4+10;
int l[N],r[N],t[N],idx;
bool vis[N],flag,flag1;//flag标记是否为完全二叉树,flag1标记是否有节点缺孩子节点 
void insert(int pos,int val)//建立二叉树 
{
	if(!vis[pos])//没有建立过点就新建一个点 
	{
		t[pos]=val;
		vis[pos]=true;
		return ;
	}
	if(val>t[pos])//左子树键值大 
	{
		if(l[pos])//存在左子树就直接遍历左子树 
			insert(l[pos],val);
		else//不存在左子树就先新建节点再遍历 
			l[pos]=++idx,insert(l[pos],val);
	}
	else
	{
		if(r[pos])//存在右子树就直接遍历右子树
			insert(r[pos],val);
		else//不存在右子树就先新建节点再遍历
			r[pos]=++idx,insert(r[pos],val);
	}
}
void bfs(int x)
{
	queue<int>q;
	q.push(x);
	printf("%d",t[x]);
	while(!q.empty())
	{
		int begin=q.front();
		if(begin!=x)
			printf(" %d",t[begin]);
		q.pop();
		if((!l[begin])&&(r[begin])) flag=false;//左孩子存在但是右孩子不存在 
		if(!flag1&&(l[begin]||r[begin])) flag=false;//前面存在缺子节点情况,但是后面又存在子节点 
		if(!l[begin]||!r[begin]) flag1=false;//出现缺子节点情况直接标记flag1=true; 
		if(l[begin]) q.push(l[begin]);
		if(r[begin]) q.push(r[begin]);
	}
}
int main()
{
	int n;
	idx=1;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int t;
		scanf("%d",&t);
		insert(1,t);
	}
	flag=flag1=true; 
	bfs(1);
	puts("");
	if(flag) printf("YES");
	else printf("NO");
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值