题目来源
题目描述
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution {
public:
bool isCompleteTree(TreeNode* root){
}
};
题目分析
实际上,完全二叉树,它是根据满二叉树来定义的
- 那什么是满二叉树呢?就是节点个数N与层数k满足:N=2^k - 1的关系的树,说白了,最后一层节点全部要满。
- 完全二叉树也就是没有满的满二叉树,它的节点在每一层一定是连续分布的。如果出现哪一层中两个非空节点间隔一个空节点,那一定不是完全二叉树。完全二叉树看起来就像是满二叉树右下角缺了一口。如下图所示:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution {
public:
bool isCompleteTree(TreeNode* root) {
}
};
题目实现
层序遍历
判断一棵树是否是完全二叉树,主要有两点:
- 按层遍历二叉树,从每层的左边向右边依次遍历所有的节点
- 如果当前节点有右孩子,但是没有左孩子,那么它一定不是完全二叉树,直接返回false
- 如果当前节点并不是左右还是都有,那么从该节点开始,之后的节点都是叶子节点
- 遍历过程中如果不返回false,遍历结束后返回true
bool isCompleteTree1(TreeNode* root){
if(root == nullptr){
return true;
}
std::queue<TreeNode *> queue;
bool leaf = false; // 是否遇到过叶子节点或者有左无右的节点
TreeNode *left = nullptr, *right = nullptr;
queue.emplace(root);
while (!queue.empty()){
root = queue.front(); queue.pop();
left = root->left, right = root->right;
if((leaf && (left != nullptr || right != nullptr)) // 如果遇到过不双全的节点,又发现当前节点不是叶节点
|| (left == nullptr && right != nullptr)){ //有右无左
return false;
}
if(left != nullptr){
queue.emplace(left);
}
if(right != nullptr){
queue.emplace(right);
}
if((left == nullptr && right == nullptr)
|| (left != nullptr && right == nullptr)){
leaf = true;
}
}
return true;
}
递归实现
从当前节点往下看,如果以当前节点为根节点的树是完全二叉树,那么有4中情况:
- 左子树是满二叉树,右子树也是满二叉树,而且左子树的高度 = 右子树的高度
- 左子树是满二叉树,右子树也是满二叉树,而且左子树高度 = 右子树高度 + 1
- 左子树是完全二叉树,右子树是满二叉树,而且左子树的高度 =右子树高度 + 1
- 左子树是满二叉树,右子树是完全二叉树,而且左子树高度 = 右子树高度
综上,对于当前节点来说,它需要问它的左右子树如下信息:
- 你是完全二叉树吗?
- 你是满二叉树吗?
- 你的高度是多少?
代码实现如下:
class Solution {
// 对每一棵子树,是否是满二叉树、是否是完全二叉树、高度
class Info{
public:
bool isFull;
bool isCBT;
int height;
explicit Info(bool full, bool cbt, int h) : isFull(full), isCBT(cbt), height(h){
}
};
Info process(TreeNode *root){
if(root == nullptr){
return Info(true, true, 0);
}
Info leftInfo = process(root->left);
Info rightInfo = process(root->right);
int height = std::max(leftInfo.height, rightInfo.height) + 1;
bool isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height;
bool isCBT = false;
// 左子树是满二叉树,右子树也是满二叉树,而且左子树的高度 = 右子树的高度
if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height) {
isCBT = true;
}
// 左子树是完全二叉树,右子树是满二叉树,而且左子树的高度 =右子树高度 + 1
else if (leftInfo.isCBT && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
isCBT = true;
}
// 左子树是满二叉树,右子树也是满二叉树,而且左子树高度 = 右子树高度 + 1
else if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
isCBT = true;
}
// 左子树是满二叉树,右子树是完全二叉树,而且左子树高度 = 右子树高度
else if (leftInfo.isFull && rightInfo.isCBT && leftInfo.height == rightInfo.height) {
isCBT = true;
}
return Info(isFull, isCBT, height);
}
public:
bool isCompleteTree(TreeNode* root) {
if (root == nullptr) {
return true;
}
return process(root).isCBT;
}
};
对数器测试(对数器不完全,待改写)
std::default_random_engine e;
std::uniform_real_distribution<float> random_float;
TreeNode* generate(int level, int maxLevel, int maxValue) {
if (level > maxLevel || random_float(e) < 0.5) {
return nullptr;
}
auto * head = new TreeNode((int) (random_float(e) * maxValue * 1.0));
head->left = generate(level + 1, maxLevel, maxValue);
head->right = generate(level + 1, maxLevel, maxValue);
return head;
}
TreeNode * generateRandomBST(int maxLevel, int maxValue) {
return generate(1, maxLevel, maxValue);
}
int main() {
e.seed(time(NULL));
int maxLevel = 5;
int maxValue = 100;
int testTimes = 1000;
for (int i = 0; i < testTimes; i++) {
TreeNode *root = generateRandomBST(maxLevel, maxValue);
if (isCompleteTree1(root) != isCompleteTree(root)) {
printf("Oops!");
return -1;
}
}
printf("ok!");
return 0;
}