LeetCode 222.完全二叉树的节点个数 三种解法


题目描述

给定一棵 完全二叉树 的根节点 root ,求出该树的节点个数


提示:以下是本篇文章正文内容,下面案例可供参考
提示:

树中节点的数目范围是[0, 5 * 10^4]
0 <= Node.val <= 5 * 10^4
题目数据保证输入的树是 完全二叉树

进阶:遍历树来统计节点是一种时间复杂度为 O(n) 的简单解决方案。你可以设计一个更快的算法吗?

解法一 层序遍历

无视题目中给的是完全二叉树选项,把它当作普通的二叉树。既然是求出结点的个数,那么只需要遍历一遍二叉树,每遍历一个结点,count值++即可。
在这里,遍历方法我选择了层序遍历,如果使用递归遍历当然也是可以的。

层序遍历法和递归遍历法的模板,可以参照代码随想录
时间复杂度:O(n)
空间复杂度:O(n)

```c++
class Solution {
public:
    int countNodes(TreeNode* root) {
        if (root == nullptr) return 0;
        queue<TreeNode* > que;
        que.push(root);
        int count = 1;
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                if (node->left) {
                    que.push(node->left);
                    count++;
                }
                if (node->right) {
                    que.push(node->right);
                    count++;
                }
            }
        } 
        return count;
    }
};

解法二 利用完全二叉树的特性

方法来源于,代码随想录
如果整个树不是满二叉树,就递归其左右孩子,直到遇到满二叉树为止。
对于下面这棵完全二叉树
在这里插入图片描述
从根节点开始递归左子树和右子树,总能递归到满二叉树的地方
在这里插入图片描述
而满二叉树的结点数量为 2^k - 1,k 表示该树的深度, 其中 root 的深度为1。

时间复杂度:O(logn * logn)
空间复杂度:O(logn)

class Solution {
public:
    int countNodes(TreeNode* root) {
        if (root == nullptr) return 0;
        TreeNode* left = root->left;
        TreeNode* right = root->right;
        int leftHeight = 0, rightHeight = 0;
        while (left) {
            left = left->left;
            leftHeight++;
        }
        while (right) {
            right = right->right;
            rightHeight++;
        }
        if (leftHeight == rightHeight) {
            return (2 << leftHeight) - 1;
        }
        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};

解法三 二分查找 + 位运算(力扣官方解法)

通过完全二叉树的定义和该题题意即可发现,这题的答案相当于求该完全二叉树的最后一层和相同深度的满二叉树的最后一层差了多少个节点。也就是说,除了最后一层,最后一层之上的几层很多遍历都是没有必要的。

只要我们从根结点开始,一直循环左结点,到达最后一层(假设为第k层,根节点为第0层),那么我们就有了节点个数的下限值,2^k 。也有了节点个数的上限值,2 ^(k+1)-1,也就是该完全二叉树是一个满二叉树。

有了上限值和下限值,在递增的区间内,要搜索某个值,自然而然就会想到二分查找。接下来就是官方解法精彩的地方。

假设有如下的二叉树,我们可以用二进制来表示要找到某个节点的路径

         1                                                    1
       /   \                                                 /  \
     2       3                                             10    11
   /  \     /  \                                          /  \   /  \
 4     5   6     7                                    100    101 110  111

次高位到最低位开始,分别表示根节点到达该节点的路径,其中 0 表示走左结点, 1 表示走右节点
比如要找到6,6的二进制是110,那么从次高位开始,分别是1和0,所以从根节点出发,先往右走,再往左走,就到达了6节点的位置。
所以我们可以通过二分法,查找最下面一层的节点是否存在,当找到最后一个存在的结点之后,就能确定整个完全二叉树的节点个数了。

class Solution {
public:
    bool isNodeExists(TreeNode* root, int depth, int mid) {
        int bits = 1 << (depth - 1); // 假设找 5 这个节点,此时 bits 为 010
        TreeNode* node = root;
        while (node != nullptr && bits > 0) {
            if (!(bits & mid)) {    // 假设找 5 这个节点, 第一次来这时, (101 & 010) = 000 ; 第二次来这时, (101 & 001) = 001
                node = node->left; // 0 走左节点
            } else {
                node = node->right; // 1 走右节点
            }
            bits >>= 1;   // bits 为 001
        }
        return node != nullptr;
    }
    int countNodes(TreeNode* root) {
        if (root == nullptr) return 0;
        int depth = 0;
        TreeNode* node = root;
        while (node->left) {
            node = node->left;
            depth++; // 树的深度,根节点为0
        }
        int low = 1 << depth; // 2^k
        int high = (1 << (depth + 1)) - 1; // 2^(k+1) - 1
        while (low < high) {
            int mid = low + ((high - low + 1) >> 1);
            if (isNodeExists(root, depth, mid)) {
                low = mid;
            } else {
                high = mid - 1;
            }
        }
        return low;
    }
};

不讲武德。。。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值