1,题目
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
2,代码求解
/*** @param {TreeNode} root* @return {number}*/
var countNodes = function(root) {const exists = (root,level,k)=>{// 最高位是 1,其余各位从高到低表示从根节点到第 k 个节点的路径,// 0 表示移动到左子节点,1 表示移动到右子节点let bits = 1 << (level -1);let node = root;while(node!=null && bits>0){if((bits & k)){node = node.right;}else{node = node.left;}// bits左移且改变值 >>= bits >>= 1;}return !(node === null);}if(root === null){return 0;}// 树的层数为 0 1 2 3 ... levellet level =0;let node = root;while(node.left != null){node = node.left;level++;}// 节点的上下限let low = 1 << level;let high = ( 1 << (level + 1)) -1;while(low < high){const mid = Math.floor((high - low +1)/2) + low;if(exists(root,level,mid)){low = mid;}else{high = mid-1;}}return low;};
3,学习与总结
3.1 树的基本知识
对于任意二叉树,都可以通过广度优先搜索或深度优先搜索计算节点个数,时间复杂度和空间复杂度都是 O(n),其中 n是二叉树的节点个数。
(1)完全二叉树节点数的计数
规定根节点位于第 0 层,完全二叉树的最大层数为 h。
(2)位运算-右移操作 (>>)
在二进制中,右移操作>>意味着将一个数的所有位向右移动指定的位数。
对于无符号整数,左侧空出的位将被填充为0。
对于有符号整数,行为可能依赖于具体的语言和编译器:一般来说,算术右移(针对有符号整数)会将左侧空出的位填充为原来的符号位(即保持正负号不变),而逻辑右移总是填充0(适用于无符号整数)。
示例
假设a的值为8,我们用a >>= 2来举例说明:
8的二进制表示为1000。
将1000向右移动2位,结果是0010。
将0010(十进制中的2)重新赋值给a。
因此,执行a >>= 2后,a的新值将是2。
(3) 过程总结
核心: 如何判断节点是否存在
如果第k个节点位于第h层,则k的二进制表示包含h+1位,其中最高位是1,其余各位从高到低表示从根节点到第k个节点的路径,且0表示左子节点,1表示右子节点。从而,通过位运算得到第k个节点对应的路径,判断该路径对应的节点是否存在;
const exists = (root,level,k)=>{let bits = 1 << (level -1);let node = root;while(node!=null && bits>0){if((bits & k)){node = node.right;}else{node = node.left;}// bits左移且改变值 >>= bits >>= 1;}return !(node === null);}
问题的转化:
首先,通过递归判断左子树是否为空,算出数的最大深度;
其次,求出该完全二叉树节点数范围;
最后,判断最后一层中的某个节点是不是最后一个节点;