题目链接: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;
}