题目描述
给定一棵 完全二叉树 的根节点 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;
}
};
不讲武德。。。