PAT甲级1110 Complete Binary Tree//判断完全二叉树

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值