题目描述
知识点
完全二叉树的性质
实现
码前思考
- 利用完全二叉树静态实现时的性质:假设当前结点编号为
x
(根结点编号必须为1),那么:
- 如果有左孩子,左孩子编号必为:
2x
;- 如果有右孩子,右孩子编号必为:
2x+1
;
- 因此,只要模仿层序遍历,比较编号是否符合上述规定就好了。即如果当前结点是左孩子,那么它一定要是父结点的编号
x
的2x
,否则说明不是完全二叉树。右孩子同理。
代码实现
//采用完全二叉树的层序遍历的性质解题
#include "bits/stdc++.h"
#include "queue"
using namespace std;
const int maxn = 25;
//采用静态的方式存储树
struct node{
//父亲结点
int father;
//左孩子
int left;
//右孩子
int right;
//编号
int no;
//构造函数
node():father(-1),left(-1),right(-1){}
}nodes[maxn];
int n;
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
char l[5];
char r[5];
scanf("%s %s",l,r);
if(l[0] != '-'){
//说明是数字
sscanf(l,"%d",&nodes[i].left);
nodes[nodes[i].left].father = i;
}
if(r[0] != '-'){
//说明是数字
sscanf(r,"%d",&nodes[i].right);
nodes[nodes[i].right].father = i;
}
}
//找到根结点
int root = -1;
for(int i=0;i<n;i++){
if(nodes[i].father == -1){
root = i;
}
}
//从根结点开始,使用层序遍历进行排查
//队列存储的是数组下标
queue<int> q;
q.push(root);
int cnt = 1;
nodes[root].no = cnt;
bool flag = true;
int last;
while(!q.empty()){
//取队列头
int cur = q.front();
last = cur;
q.pop();
if(cur != root){
//首先设定它的顺序
cnt++;
nodes[cur].no = cnt;
//看是否与其父节点对应
//首先看其是左节点还是右结点
if(cur == nodes[nodes[cur].father].left){
if((nodes[cur].no) != (nodes[nodes[cur].father].no)*2){
flag = false;
break;
}
}else{
if((nodes[cur].no) != (nodes[nodes[cur].father].no)*2+1){
flag = false;
break;
}
}
//压栈左右节点
if(nodes[cur].left != -1){
q.push(nodes[cur].left);
}
if(nodes[cur].right != -1){
q.push(nodes[cur].right);
}
}else{
//是根结点,直接压栈左右节点
if(nodes[cur].left != -1){
q.push(nodes[cur].left);
}
if(nodes[cur].right != -1){
q.push(nodes[cur].right);
}
}
}
if(flag == true){
printf("YES %d",last);
}else{
printf("NO %d",root);
}
return 0;
}
码后反思
- 注意输入要以
字符数组char[]
接收,不能使用字符char
,因为编号可以大于等于10的! - 需要记住完全二叉树的静态实现的性质,而且《算法笔记》上说根结点编号必须从1开始。此外,不要直接使用
当前结点的值/2
==父节点的值 进行相等比较。否则,在只有根结点和它的右孩子时会出现错误的。要使用在前面说的那种方式比较。 - 柳婼的代码使用
dfs
来进行解题,她利用的性质也是完全二叉树编号的性质,只不过她是判断,编号值是否大于结点数。#include <iostream> using namespace std; struct node{ int l, r; }a[100]; int maxn = -1, ans; void dfs(int root, int index) { if(index > maxn) { maxn = index; ans = root; } if(a[root].l != -1) dfs(a[root].l, index * 2); if(a[root].r != -1) dfs(a[root].r, index * 2 + 1); } int main() { int n, root = 0, have[100] = {0}; cin >> n; for (int i = 0; i < n; i++) { string l, r; cin >> l >> r; if (l == "-") { a[i].l = -1; } else { a[i].l = stoi(l); have[stoi(l)] = 1; } if (r == "-") { a[i].r = -1; } else { a[i].r = stoi(r); have[stoi(r)] = 1; } } while (have[root] != 0) root++; dfs(root, 1); if (maxn == n) cout << "YES " << ans; else cout << "NO " << root; return 0; }
上面有一个重要的知识点stoi()
,用于将字符串转成数字。
二刷代码
使用了dfs来解题,而不是层序遍历
//采用完全二叉树的层序遍历的性质解题
#include "bits/stdc++.h"
using namespace std;
const int maxn = 25;
//采用静态的方式存储树
struct node{
//父亲结点
int father;
//左孩子
int left;
//右孩子
int right;
//编号
int no;
//构造函数
node():father(-1),left(-1),right(-1){}
}nodes[maxn];
int n;
bool flag=true;
int last;
void dfs(int root,int idx){
if(root==-1){
return;
}else{
if(idx>n){
flag = false;
}else if(idx==n){
last = root;
}
dfs(nodes[root].left,idx*2);
dfs(nodes[root].right,idx*2+1);
}
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
char l[5];
char r[5];
scanf("%s %s",l,r);
if(l[0] != '-'){
//说明是数字
sscanf(l,"%d",&nodes[i].left);
nodes[nodes[i].left].father = i;
}
if(r[0] != '-'){
//说明是数字
sscanf(r,"%d",&nodes[i].right);
nodes[nodes[i].right].father = i;
}
}
//找到根结点
int root = -1;
for(int i=0;i<n;i++){
if(nodes[i].father == -1){
root = i;
}
}
//尝试使用dfs来操作
dfs(root,1);
if(flag == true){
printf("YES %d",last);
}else{
printf("NO %d",root);
}
return 0;
}