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 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:
[0, 5 * 104]
.0 <= Node.val <= 5 * 104
太难了,这题太难了!也可能是因为好久好久没做tree了。
首先先了解一下这题的complete binary tree的概念,就是所有的node都是从左到右排序。然后我们再来复习一下full binary tree的概念,就是每个node的左右都有node或者都没有node。
这题让我们求一个complete binary tree里有多少个nodes。最无脑的方法就是全部遍历一遍计算有多少个。但是这里可以用到complete binary tree的性质来优化时间复杂度,参考了:https://leetcode.com/problems/count-complete-tree-nodes/solutions/61958/concise-java-solutions-o-log-n-2/
首先我们需要一个helper function求height,这里的height定义为,如果是空就是-1,一个root的树height为0。
这篇solution里讲的我觉得有点难理解,它是通过不停往左来得到height。整个逻辑在于,先求出当前root的height比如是h,再看看root.right的height,只有两种情况:
1. h - 1:说明root.left和root.right高度一样,那么root.left必然是full binary tree with all nodes at leaf level(此时root.left的高度是h - 1),那么此时root的countNode()就是左边的node个数(2^h - 1)+右边的node个数(countNode(root.right))+root(1)
2. h - 2:此时root.left的height必然还是h -1,说明右边比左边矮,那就说明最后一个node结束在左边,于是root.right必然是full binary tree with all nodes at leaf level(此时root.right的高度是h - 2),那么此时root的countNode()就是右边的node个数(2^(h-1) - 1)+右边的node个数(countNode(root.left))+root(1)
嗯……然后用left shift代替了Math.pow(2, h)。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int countNodes(TreeNode root) {
int h = height(root);
if (h < 0) {
return 0;
}
// left subtree has same height as right subtree, then left subtree should be full with height h - 1
if (height(root.right) == h - 1) {
return (1 << h) + countNodes(root.right);
} else {
// left subtree is higher than right subtree, then right subtree should be full with height h - 2
return (1 << h - 1) + countNodes(root.left);
}
}
private int height(TreeNode root) {
return root == null ? -1 : 1 + height(root.left);
}
}
然后又看了底下评论区的一个youtube视频,感觉讲的更清晰:https://youtu.be/4wPlA_InnGY
思想主要是计算一直往左的height和一直往右的height,比较left_h和right_h是否相等,如果相等的话说明是full binary tree with all nodes at leaf level(整棵树高度为left_h),如果不相等的话就对left和right分别进行countNode()再+1作为返回值。感觉这个方法要简单直观一些。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int countNodes(TreeNode root) {
if (root == null) {
return 0;
}
int left = leftHeight(root);
int right = rightHeight(root);
if (left == right) {
return (1 << left + 1) - 1;
}
return countNodes(root.left) + countNodes(root.right) + 1;
}
private int leftHeight(TreeNode root) {
return root == null ? -1 : 1 + leftHeight(root.left);
}
private int rightHeight(TreeNode root) {
return root == null ? -1 : 1 + rightHeight(root.right);
}
}
这个solutions还提供了iterative解法,累了,下次一定。