将一系列给定数字顺序插入一个初始为空的二叉搜索树(定义为左子树键值大,右子树键值小),你需要判断最后的树是否一棵完全二叉树,并且给出其层序遍历的结果。
输入格式:
输入第一行给出一个不超过20的正整数N
;第二行给出N
个互不相同的正整数,其间以空格分隔。
输出格式:
将输入的N
个正整数顺序插入一个初始为空的二叉搜索树。在第一行中输出结果树的层序遍历结果,数字间以1个空格分隔,行的首尾不得有多余空格。第二行输出YES
,如果该树是完全二叉树;否则输出NO
。
输入样例1:
9
38 45 42 24 58 30 67 12 51
输出样例1:
38 45 24 58 42 30 12 67 51
YES
输入样例2:
8
38 24 12 45 58 67 42 51
输出样例2:
38 45 24 58 42 12 67 51
NO
题解
这个题就是欺负人。你知道什么是二叉搜索树就能做,不知道就不能做。
二叉搜索树
二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。是数据结构中的一类。在一般情况下,查询效率比链表结构要高。
一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的结点。
来源:百度百科
就是在二叉树基础上的纵深的概念。
看了一遍,顺便按照案例再手写了几遍就能找到思路了。
没错,就是dfs建树。
cin>>root; //读入的第一个就是根节点
for(int i=1;i<=N-1;i++)
{
cin>item;
dfs(item,root);
}
void dfs(int i,int root) //模拟手写判断
{
if(i>root) //当前节点比根节点大(注意题中要求大的在左边)
{
if(p[root].l==0) p[root].l = i; //根节点没有左儿子则当前节点成为左儿子
else dfs(i,p[root].l); //当前节点有左儿子,递归下去
}
else if(i<root)
{
if(p[root].r==0) p[root].r = i;
else dfs(i,p[root].r);
}
}
建好后一个难点就是判断是不是完全二叉树。
百度百科也有记载。
判断一棵树是否是完全二叉树的思路
1>如果树为空,则直接返回错
2>如果树不为空:层序遍历二叉树
2.1>如果一个结点左右孩子都不为空,则pop该节点,将其左右孩子入队列;
2.1>如果遇到一个结点,左孩子为空,右孩子不为空,则该树一定不是完全二叉树;
2.2>如果遇到一个结点,左孩子不为空,右孩子为空;或者左右孩子都为空;则该节点之后的队列中的结点都为叶子节点;该树才是完全二叉树,否则就不是完全二叉树;
来源:百度百科
于是就在队列输出层序遍历中添加了些东西。
ac代码
#include<bits/stdc++.h>
using namespace std;
struct node{ //二叉树节点
int l,r;
}p[200000];
void dfs(int i,int root) //搭建树
{
if(i>root)
{
if(p[root].l==0) p[root].l = i;
else dfs(i,p[root].l);
}
else if(i<root)
{
if(p[root].r==0) p[root].r = i;
else dfs(i,p[root].r);
}
}
queue<int>q;
vector<int>vec;
int main()
{
int N,item,root;
cin>>N;
cin>>root;
for(int i=1;i<=N-1;i++)
{
cin>>item;
dfs(item,root);
}
//层序遍历
int flag=1,ye=0;
q.push(root);
while(q.size())
{
int head = q.front();
vec.push_back(head);
if(p[head].l!=0 &&p[head].r!=0) //用于判读是否是完全二叉树(完全依托百科思路)
{
q.push(p[head].l); //左右儿子全有
q.push(p[head].r);
if(ye!=0) flag=0;
}
else if(p[head].l==0&&p[head].r!=0)
{
q.push(p[head].r); //只有右儿子,直接判错
flag = 0;
}
else if(p[head].l!=0 && p[head].r==0)
{
q.push(p[head].l);
if(ye!=0) flag=0;
ye++; //开启--->开启之后就只能接收无子节点,一旦遇到其他的就不是完全二叉
} //ye非0就是开启了(第3/4种都可以开启)
else
{
ye++; //开启
}
q.pop();
}
for(int i=0;i<vec.size();i++)
{
if(i==0) cout<<vec[i];
else cout<<" "<<vec[i];
}
cout<<endl;
//判断完全二叉树
if(flag==1) cout<<"YES\n";
else cout<<"NO\n";
return 0;
}
但是啊,兄弟们,有木有觉得这个完全二叉树判断写起来很费劲呢?
我是真的感觉费劲,要想好多,还要调试,我才过得。
我在网上找到了更好的办法。
链接:https://blog.youkuaiyun.com/wangjingyihhh/article/details/84501154
虽然它用的是指针,但可以吸收思想。
新的判断思路:
队列一步步向下走时,将当前节点的左右子树都加入队列(哪怕它没有,也就是为0),直到队首节点为0,遍历结束。
此时,若所有题中节点已被遍历(队列经过N个点),则是完全二叉树。
若还有题中节点未被遍历,则不是完全二叉树。
手写画画自可发现其正确性。
所以,我只需要进行记数,最后与N进行比对就好啦。
我想把记数写入层序遍历,使代码得到体量上的最大优化。却产生了有问题的代码。
问题代码
#include<bits/stdc++.h>
using namespace std;
struct node{ //二叉树节点
int l,r;
}p[200000];
void dfs(int i,int root) //搭建树
{
if(i>root)
{
if(p[root].l==0) p[root].l = i;
else dfs(i,p[root].l);
}
else if(i<root)
{
if(p[root].r==0) p[root].r = i;
else dfs(i,p[root].r);
}
}
queue<int>q;
vector<int>vec;
int main()
{
int N,item,root;
cin>>N;
cin>>root;
for(int i=1;i<=N-1;i++)
{
cin>>item;
dfs(item,root);
}
//层序遍历
int cou=0;
q.push(root);
while(q.size())
{
int head = q.front();
if(head==0) break;
cou++;
vec.push_back(head);
q.push(p[head].l);
q.push(p[head].r);
q.pop();
}
for(int i=0;i<vec.size();i++)
{
if(i==0) cout<<vec[i];
else cout<<" "<<vec[i];
}
cout<<endl;
//判断完全二叉树
if(cou==N) cout<<"YES\n";
else cout<<"NO\n";
return 0;
}
经过检查,这个break,能够保证跳出循环,使cou不再增加,最终参与判断。
但若题中为非完全二叉树,这个break就会导致有一些数据还没有经层序遍历就结束了,最终的层序遍历就会少几个数。
为了避免这一点,我只能写两次遍历,一个遍历,一个判断。我把判断封装成了函数。
新的ac代码
#include<bits/stdc++.h>
using namespace std;
int cou=0; //判断
struct node{
int l,r;
}p[200000];
queue<int>q;
vector<int>vec;
void dfs(int i,int root)
{
if(i>root)
{
if(p[root].l==0) p[root].l = i;
else dfs(i,p[root].l);
}
else if(i<root)
{
if(p[root].r==0) p[root].r = i;
else dfs(i,p[root].r);
}
}
void judge(int root)
{
q.push(root);
while(q.size())
{
int head = q.front();
if(head==0) break;
cou++;
q.push(p[head].l);
q.push(p[head].r);
vec.push_back(head);
q.pop();
}
}
int main()
{
int N,item,root;
cin>>N;
cin>>root;
for(int i=1;i<=N-1;i++)
{
cin>>item;
dfs(item,root);
}
//层序遍历
q.push(root);
while(q.size())
{
int head = q.front();
if(p[head].l!=0) q.push(p[head].l);
if(p[head].r!=0) q.push(p[head].r);
vec.push_back(head);
q.pop();
}
for(int i=0;i<vec.size();i++)
{
if(i==0) cout<<vec[i];
else cout<<" "<<vec[i];
}
cout<<endl;
judge(root);
//判断完全二叉树
if(cou==N) cout<<"YES\n";
else cout<<"NO\n";
return 0;
}