Given a complete binary tree, count the number of nodes.
Note:
Definition of a complete binary tree from Wikipedia:
In a complete binary tree every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible. It can have between 1 and 2h nodes inclusive at the last level h.
Example:
Input: 1 / 2 3 / / 4 5 6 Output: 6
完全二叉树的节点个数。题意是给一个完全二叉树,请求出节点个数。
比较直观的思路是可以用递归。因为一个完美二叉树的节点个数是2^h - 1,2的h次方 - 1,h是树的高度。如果根节点的左子树和右子树的高度一样,则说明左子树一定为满二叉树。如果左子树和右子树的高度不一样,则右子树一定不是满二叉树,左子树也有可能不是满二叉树,就需要递归到更小的子树去看到底哪个子树不是满二叉树。
时间O(n)
空间O(logn) - O(h) - 树的高度
Java实现
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public int countNodes(TreeNode root) { 12 int left = leftDepth(root); 13 int right = rightDepth(root); 14 // 如果根节点的左子树深度等于右子树深度,则说明左子树为满二叉树 15 // 否则就要递归去找到底哪个子树不是满二叉树 16 if (left == right) { 17 return (int) Math.pow(2, left) - 1; 18 } else { 19 return 1 + countNodes(root.left) + countNodes(root.right); 20 } 21 } 22 23 private int leftDepth(TreeNode root) { 24 int depth = 0; 25 while (root != null) { 26 root = root.left; 27 depth++; 28 } 29 return depth; 30 } 31 32 private int rightDepth(TreeNode root) { 33 int depth = 0; 34 while (root != null) { 35 root = root.right; 36 depth++; 37 } 38 return depth; 39 } 40 }
注意这个题的tag是binary search二分法,同时二分法也是最优解,所以这里我也给出二分法的解法,参考了这个帖子。思路是首先拿到树的最大深度,这个不难,只要不停地往左孩子遍历就行。其次,如果是一个满二叉树(complete binary tree),节点数是2^depth - 1。按照例子,如果是一个高度为3的树,节点个数就是2^3 - 1 = 7;同时最后一层的节点数应该是2^(depth - 1) = 2^2 = 4。
但是如果只是一个完全二叉树,最后一层的节点数很有可能是缺失的,此时就需要用二分法找最后一层到底有几个节点了。这里需要写一个helper函数帮助做二分法,因为每一层的节点数是跟当前的深度有关所以当前层的节点数的范围应该在0 ~ (2^depth - 1)之间。所以二分法找的时候,比如一个高度为3的树如果是满的,理论上最低一层应该有8个节点,则去判断是否有第4个节点,若有则往右子树走,若没有则往左子树走。最后helper函数返回的是最后一层处于位置i的节点是否存在。
最后着重解释一下exist函数。还是给定了上下界left = 0, right = 2^depth - 1。这个函数是为了检查某一个节点是否存在的。检查的方法其实是类似DFS,从深度为0的地方开始,在已经知道节点index的情况下,带入tree去检查,如果index大于当前层中间的node,则去右子树看,反之则去左子树看。
时间O(logn * logn)
空间O(1)
Java实现
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode() {} 8 * TreeNode(int val) { this.val = val; } 9 * TreeNode(int val, TreeNode left, TreeNode right) { 10 * this.val = val; 11 * this.left = left; 12 * this.right = right; 13 * } 14 * } 15 */ 16 class Solution { 17 public int countNodes(TreeNode root) { 18 // corner case 19 if (root == null) { 20 return 0; 21 } 22 int d = computeDepth(root); 23 if (d == 0) { 24 return 1; 25 } 26 27 // normal case 28 int left = 1; 29 int right = (int) Math.pow(2, d) - 1; 30 while (left <= right) { 31 int mid = left + (right - left) / 2; 32 if (exists(root, d, mid)) { 33 left = mid + 1; 34 } else { 35 right = mid - 1; 36 } 37 } 38 return (int) Math.pow(2, d) - 1 + left; 39 } 40 41 private int computeDepth(TreeNode root) { 42 int depth = 0; 43 while (root.left != null) { 44 depth++; 45 root = root.left; 46 } 47 return depth; 48 } 49 50 private boolean exists(TreeNode root, int depth, int index) { 51 int left = 0; 52 int right = (int) Math.pow(2, depth) - 1; 53 int mid; 54 for (int i = 0; i < depth; i++) { 55 mid = left + (right - left) / 2; 56 if (index <= mid) { 57 root = root.left; 58 right = mid; 59 } else { 60 root = root.right; 61 left = mid; 62 } 63 } 64 return root != null; 65 } 66 }