Given a tree, you are supposed to tell if it is a complete binary tree.
Input Specification:
Each input file contains one test case. For each case, the first line gives a positive integer N (≤20) which is the total number of nodes in the tree – and hence the nodes are numbered from 0 to N−1. Then N lines follow, each corresponds to a node, and gives the indices of the left and right children of the node. If the child does not exist, a - will be put at the position. Any pair of children are separated by a space.
Output Specification:
For each case, print in one line YES and the index of the last node if the tree is a complete binary tree, or NO and the index of the root if not. There must be exactly one space separating the word and the number.
Sample Input 1:
//9
//7 8
//- -
//- -
//- -
//0 1
//2 3
//4 5
//- -
//- -
Sample Output 1:
YES 8
Sample Input 2:
//8
//- -
//4 5
//0 6
//- -
//2 3
//- 7
//- -
//- -
Sample Output 2:
NO 1
思路
题目要求判断一棵树是否为完全二叉树。我的思路是对树进行判断:
- 首先如果一个结点有右子结点但是没有左子结点,那它就不是完全二叉树
- 如果一个结点有左子结点且没有右子结点,那这个结点之后的所有结点都必须是叶结点
- 如果一个结点是叶结点,那它之后的所有结点都是叶结点
但是树的结点编号是混乱的,比如题目给的样例
1
/ \
4 5
/ \ \
2 3 7
/ \
0 6
2右边的结点是3,3右边的结点是7,毫无规律可言,我该怎么找办法求"一个结点之后的结点"呢?
这里我用了水平遍历序列来辅助,比如上面树的水平遍历序列是:
水平序列:1 4 5 2 3 7 0 6
数组下标:0 1 2 3 4 5 6 7
结点2对应的下标是3,所以2之后的结点可以用下标4567表示,接着就是对上面三个条件进行判断即可
#include <iostream>
#include <cstdlib>
#include <vector>
#include <string>
#include <queue>
using namespace std;
struct node
{
int top = -1;
int left = -1;
int right = -1;
bool isRoot = true;
bool isLeaf = true;
};
void level_order(vector<node> &, int);
int index;
int levelOrder[21];
int main()
{
int n;
cin >> n;
vector<node> tree(n);
for (int i = 0; i < n; i++)
{
tree[i].top = i;
string left, right;
cin >> left >> right;
if (left != "-")
{
tree[i].left = stoi(left);
//有子结点说明不是叶结点
tree[i].isLeaf = false;
//子结点有父结点,说明子结点不可能成为根结点
tree[tree[i].left].isRoot = false;
}
if (right != "-")
{
tree[i].right = stoi(right);
tree[i].isLeaf = false;
tree[tree[i].right].isRoot = false;
}
}
int root;
//找根结点
for (int i = 0; i < n; i++)
{
if (tree[i].isRoot)
{
root = i;
break;
}
}
level_order(tree, root);
bool flag = true;
for (int i = 0; i < index; i++)
{
//如果一个结点有右子结点但是没有左子结点,那它就不是完全二叉树
if (tree[levelOrder[i]].left == -1 && tree[levelOrder[i]].right != -1)
{
flag = false;
break;
}
//如果一个结点有左子结点且没有右子结点,或者本身是叶结点
//那这个结点之后的所有结点都必须是叶结点
if (tree[levelOrder[i]].left != -1 && tree[levelOrder[i]].right == -1 ||
tree[levelOrder[i]].isLeaf)
{
for (int j = i + 1; j < index; j++)
{
if (!tree[levelOrder[j]].isLeaf)
{
flag = false;
break;
}
}
}
}
if (flag)
cout << "YES " << tree[levelOrder[index - 1]].top << endl;
else
cout << "NO " << root << endl;
system("pause");
return 0;
}
void level_order(vector<node> &t, int root)
{
queue<node> que;
que.push(t[root]);
while (!que.empty())
{
int size = que.size();
for (int i = 0; i < size; i++)
{
node head = que.front();
if (head.left != -1)
que.push(t[head.left]);
if (head.right != -1)
que.push(t[head.right]);
levelOrder[index++] = head.top;
que.pop();
}
}
}
方法拓展
在网上冲浪也发现了一个好办法:
循环将所有结点押入队列,包括空结点。然后每次都检查队列的头,如果头为空就退出循环,这时如果队列中还有元素就说明不是完全二叉树
1
/ \
2 3
/ \
4 5
队列情况(左出右进):
1
2 3
3 4 5
4 5 NULL NULL
5 NULL NULL NULL NULL
NULL NULL NULL NULL NULL NULL
1
/ \
2 3
/ \
4 5
队列情况(左出右进):
1
2 3
3 NULL NULL
NULL NULL 4 5
队列头为NULL,退出循环
发现队列中还有元素,不是完全二叉树
代码:
bool judge(tree* root)
{
queue<node*> q;
q.push(root);
tree* phead = q.front();
while (phead != NULL)
{
q.pop();
q.push(phead -> left);
q.push(phead -> right);
phead = q.front();
}
while (!q.empty())
{
if (q.front() != NULL)
return false;
q.pop();
}
return true;
}