L3-010 是否完全二叉搜索树(30 分)
将一系列给定数字顺序插入一个初始为空的二叉搜索树(定义为左子树键值大,右子树键值小),你需要判断最后的树是否一棵完全二叉树,并且给出其层序遍历的结果。
输入格式:
输入第一行给出一个不超过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
思路:
一开始想的是只要没有只有右叶子的节点就可以,显然是不对的(其实没那么显然,要自己动手画画),如果左、右子树都完全,但深度相差超过2就符合我的条件,但不是完全二叉树。
后来想的是标号,但这种方法最底层节点的NULL和缺的点的NULL是一样的,无法区分。
最后看了题解:还是用广搜的思路,用队列把所有节点都层序入队,所有的NULL也入队(其实队里直存指针),如果遇到第一个NULL就退出,开始遍历队列里剩下的指针,如果全为NULL,就是完全二叉树;如果还有指针指向节点就不是完全二叉树。
下面来简单分析一下,上图
下文中的c点表示当前节点。
首先层序遍历,所有节点肯定只访问一遍。这种算法的特点是当把c从队列里拿出来时,它的前面节点的下一层肯定都在队列里面了。所以如果c为空,那么要想是完全二叉树,它的后面就不应该有节点了。有朋友可能会说,如果他的前面恰好都为空,而c点的后面节点还有节点呢,这种情况显然不可能,因为c的下一层后半部分要想有节点,那c的后面一点有节点,这样显然不是二叉树。
总结:
1.这种判断点是否按顺序出现,应该想到队列的(按顺序就是排队嘛),因为这是一个天然的队,如果依次入队,遇到第一个空洞时,后面就不应该有节点了,当然只想到队还是不好做,细节还是要多练,多思考才能做出来。比如这个题要把c点的左右节点统统入队,c点第一次为NULL时就退出循环,这点也是很难想的。
2.队的入队,和出队一定要分开想清楚,这样不容易想当然。分别时两次操作,如果混为一谈,或者指向入不想出,那就没了队的优点了。
3.自己脑补模拟队时,当入队出现特殊元素时,和出队出现特殊元素时也要区分,这个题就是很好的例子。
#include<iostream>
#include<queue>
using namespace std;
struct node{
node *l, *r;
int val;
int num;
};
node* createRoot(int val);
bool isCmplt(node* r);
bool insrt(node *r, int val);
bool del(int val);
void levelshow(node* r);
int N;
int main(){
int a;
cin >> N >> a;
node *root = createRoot(a);
for(int i = 1; i < N; i++){
cin >> a;
insrt(root, a);
}
levelshow(root);
if(isCmplt(root))
cout << "YES" << endl;
else
cout << "NO" << endl;
return 0;
}
node* createRoot(int val){
node *root = new node();
root->val = val;
root->l = NULL;
root->r = NULL;
return root;
}
bool insrt(node *cur, int val){
if(val > cur->val){
if(cur->l != NULL)
insrt(cur->l, val);
else{
node *tmp = new node();
tmp->l = NULL;
tmp->r = NULL;
tmp->val = val;
cur->l = tmp;
return true;
}
}
else if(val < cur->val){
if(cur->r != NULL){
insrt(cur->r, val);
}
else{
node *tmp = new node();
tmp->l = NULL;
tmp->r = NULL;
tmp->val = val;
cur->r = tmp;
return true;
}
}
else{
cout << "already exist" << endl;
return false;
}
}
void levelshow(node *cur){
queue<node*> q;
q.push(cur);
int flag = 0;
while(!q.empty()){
cur = q.front();
cout << cur->val;
if( (q.size() > 1 || flag == 0) && N > 1){
cout << " ";
flag = 1;
}
q.pop();
if(cur->l != NULL)
q.push(cur->l);
if(cur->r !=NULL)
q.push(cur->r);
}
cout << endl;
}
bool isCmplt(node *cur){
queue<node*> q;
q.push(cur);
while(cur != NULL){
q.push(cur->l);
q.push(cur->r);
q.pop();
cur = q.front();
}
while(!q.empty()){
cur = q.front();
if(cur != NULL)
return false;
q.pop();
}
return true;
}