完全二叉树结点数
完全二叉树结点数
问题描述
222. 完全二叉树的节点个数
给你一棵完全二叉树的根节点 root ,求出该树的节点个数。
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
示例:
输入:root = [1,2,3,4,5,6]
输出:6
分析问题
对于任意一颗二叉树来说,我们都可以使用深度优先搜索和广度优先搜索来计算节点的个数,其时间复杂度和空间复杂度都是O(n)。那对于一颗完全二叉树来说,有什么更好的求解方式吗?
根据完全二叉树的特征可知,完全二叉树的最左边的节点一定位于树的最后一层,因此从根节点出发,每次访问左子节点,直到遇到叶子节点,该叶子节点即为完全二叉树的最左边的节点,经过的路径长度即为二叉树的最大层数。
我们假设完全二叉树的根节点位于第0层,完全二叉树的最大层数是h,当0<= i < h时,第i层包含2i个节点,最后一层包含的节点数为1~2h个节点。因此对于最大层数为h的完全二叉树来说,节点个数在[2h, 2h+1-1]范围内,我们可以在该范围内通过二分查找的方式得到完全二叉树的节点个数。
具体来说,我们可以在[2h, 2h+1-1]范围内进行二分查找。我们根据范围的上下界得到当前需要判断的节点个数 k,如果第 k个节点存在,则节点个数一定大于或等于 k,如果第 k个节点不存在,则节点个数一定小于 k,由此可以将查找的范围缩小一半,直到得到节点个数。
下面我们来看一下如何判断第k个节点是否存在?
假设第k个节点位于第h层,则数k的二进制表示包含h+1位,其中最高位是1,其余各位从高到低表示从根节点到第 k个节点的路径,0 表示移动到左子节点,1 表示移动到右子节点。通过位运算得到第 k个节点对应的路径,通过判断该路径对应的节点是否存在,即可判断第 k 个节点是否存在。
下面我们来看一下代码的实现。
class Solution:
def countNodes(self, root):
if not root:
return 0
#求数的层数
level=0
node=root
while node.left:
level=level+1
node=node.left
#完全二叉树的节点个数范围是[2^h,(2^h+1)-1]
low=2**level
high=2**(level+1)-1
#二叉查找
while low<high:
mid=(low+high+1)//2
#判断结点是否存在,如果存在,
#则节点数在[mid,high]范围中,否则在[low,mid-1]
if self.exist(root,level,mid):
low=mid
else:
high=mid-1
return low
def exist(self,root,level,k):
#bits用来控制判断走到了对应的二进制的哪一位
#比如要判断节点5是否存在,它对应的二进制是101,我们把最高位去掉,从第二位开始判断
#所以我们需要一个bits为010和它求与,即010 & 101,发现第二位是0,说明从根节点开始,第一步向左走
#然后bits右移一位,变成了001,然后继续求与,即 001 & 101,发现第三位是1,所以向右走。
#最后bit为0,说明已经找到编号为5的这个节点相对于根节点的位置
#看这个节点是不是空,exist返回true,如果是空,exist返回false
bits= 1 << (level-1)
node=root
while node and bits > 0:
if bits & k == 0:
node=node.left
else:
node=node.right
bits >>=1
return node!=None