https://leetcode.cn/problems/count-complete-tree-nodes/description/?envType=study-plan-v2&envId=top-interview-150
进阶算法挺难想到的,我建议可以画一个二叉树给节点标号(下标从1开始)后再找规律,将从根出发到目标点的路径和目标点的标号联系一下。
思路:因为是完全2叉树,所以我们可以直接知道前h-1层的节点数量,我们一直往根节点的右子树方向走可以最快到底得到深度h1,一直往左走可以最慢到底得到深度h2,那么h2-h1就可以知道不完全填充的层数。现在的问题就变成了怎么求出这层的节点数量,因为完全二叉树的性质所以最后一层的节点标号是在[2**h, 2**(h+1)-1],我们可以通过二分确定一个节点存在,再通过该标号的二进制序列号来从根节点出发寻找(1代表向右走,0向左走)该节点是否存在。
public class Main {
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;
}
}
public int countNodes(TreeNode root) {
if(root == null) return 0;
int h1 = getLeftDeep(root);
int h2 = getRightDeep(root);
if(h1 == h2) {// 满二叉树
return (int) Math.pow(2, h1) - 1;
}
// 二分寻找节点是否存在
int l =(int) Math.pow(2, h2);
int r = (int) Math.pow(2, h1) - 1;
while(l < r) {
int mid = l + (r - l + 1) / 2;
if(exist(root, mid, h1)) {
l = mid;
} else {
r = mid - 1;
}
}
return l;
}
private boolean exist(TreeNode root, int mid, int h1) {
Stack<Integer> st = new Stack<>();
while(mid > 0) {
st.push(mid & 1);
mid >>= 1;
}
TreeNode node = root;
st.pop();// 要弹出一个是因为根节点的标号为1,我们已经取出来了所以要弹出一个
while(!st.isEmpty()) {
if(st.pop() == 1) {
node = node.right;
} else {
node = node.left;
}
if(node == null) return false;
}
return true;
}
public int getLeftDeep(TreeNode node) {
if(node.left == null) return 1;
return getLeftDeep(node.left) + 1;
}
public int getRightDeep(TreeNode node) {
if(node.right == null) return 1;
return getRightDeep(node.right) + 1;
}
}