222. Count Complete Tree Nodes
Given the root of a complete binary tree, return the number of the nodes in the tree.
According to Wikipedia, every level, except possibly the last, is completely filled in a complete binary tree, and all nodes in the last level are as far left as possible. It can have between 1 and 2 h 2^h 2h nodes inclusive at the last level h.
Design an algorithm that runs in less than O(n) time complexity.
Example 1:
Input: root = [1,2,3,4,5,6]
Output: 6
Example 2:
Input: root = []
Output: 0
Example 3:
Input: root = [1]
Output: 1
Constraints:
- The number of nodes in the tree is in the range [ 0 , 5 ∗ 1 0 4 0, 5 * 10^4 0,5∗104].
- 0 < = N o d e . v a l < = 5 ∗ 1 0 4 0 <= Node.val <= 5 * 10^4 0<=Node.val<=5∗104
- The tree is guaranteed to be complete.
From: LeetCode
Link: 222. Count Complete Tree Nodes
Solution:
Ideas:
Main Idea:
The main idea is to utilize a combination of the properties of a binary tree and binary search. Given that the tree is complete, we can determine the depth (or height) of the tree and then perform a binary search to find the number of nodes in the last level.
Code Explanation:
1. depth function:
- Computes the depth (or height) of the tree by simply traversing down the leftmost path until we hit a null node. This takes O ( h ) O(h) O(h) time where h is the height of the tree.
2. exists function:
- This function checks if a node exists at a given index on the last level of the tree.
- The function uses binary representation of the index to traverse from root to the desired node. For each bit in the binary representation:
- If the bit is 0, move to the left child.
- If the bit is 1, move to the right child.
- At the end of this traversal, if the node exists, the function returns true, otherwise false.
3. countNodes function:
- First, if the tree is empty, it immediately returns 0.
- It then computes the depth of the tree.
- If the depth is 1 (only root exists), it returns 1.
- For trees with depth greater than 1, it performs a binary search to find the rightmost node’s index on the last level:
- The search range for the index is from 0 to 2 h − 1 2^h−1 2h−1, where h is the depth of the tree minus 1 (since we’re only interested in the last level).
- Using the exists function, it checks the existence of a node for a given index.
- If the node exists for the current pivot index, it adjusts the search range to the right half, otherwise, it adjusts to the left half.
- Once the binary search completes, the left pointer indicates the total number of nodes in the last level.
- The total count of nodes in the tree is the sum of nodes in the first h − 1 h−1 h−1 levels (which is 2 h − 1 − 1 2^{h−1}−1 2h−1−1) and the number of nodes in the last level.
Code:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
int depth(struct TreeNode* root) {
int d = 0;
while (root) {
root = root->left;
d++;
}
return d;
}
bool exists(int idx, int d, struct TreeNode* root) {
int left = 0, right = (1 << d) - 1;
for (int i = 0; i < d; i++) {
int pivot = left + (right - left) / 2;
if (idx <= pivot) {
root = root->left;
right = pivot;
} else {
root = root->right;
left = pivot + 1;
}
}
return root != NULL;
}
int countNodes(struct TreeNode* root) {
if (!root) return 0;
int d = depth(root) - 1;
if (d == 0) return 1;
int left = 0, right = (1 << d) - 1;
while (left <= right) {
int pivot = left + (right - left) / 2;
if (exists(pivot, d, root)) {
left = pivot + 1; // Adjust here. Move to the next possible node.
} else {
right = pivot - 1;
}
}
return (1 << d) - 1 + left; // left now represents the count of nodes in the last level.
}