Skip to content

力扣链接:222.完全二叉树的节点个数

难度:⭐

✏️ 方法一:我的掘金文章详细解答

解题关键词:二叉树二分查找位运算

解题思路:使用层序遍历的顺序把每个节点从 1 开始往后编号,每个编号变成二进制,除去第一位开始看,就是根节点到每个节点的顺序,0代表向左走,1表示向右走。一开始得到最大层级后,就可以知道最下面一层的编号是多少到多少(第h层(h从0开始):最小编号是 2的h次方,最大编号是 2的(h+1)次方 - 1),然后使用二分法,逐渐判断每个节点是否存在,用一个 exist 的函数,来判断,实现使用位运算,从二进制编号从第二位开始往后面取,最后看节点是否存在。

WARNING

从第二位往后取的逻辑就是每一层需要的 bits,就是每一层除去第一位,然后需要多少位拿到路径,层数h从0开始计算,第h层需要的 bits = 2的(h-1)次方,然后每判断一位,这个 bits 要右移一次,然后拿到下一位的路径值。

typescript
/**
 * 判断一个节点(k)是否存在的函数
 * root:根节点
 * level:最后一层
 * k:节点
 */
const exists = (root, level, k) => {
  // 除去第一位二进制数,然后需要看的位数,第 level 层需要看 2的(level-1)次方的位数
  let bits = 1 << (level - 1);
  let node = root;

  // 当节点不为空,且还没有拿完后面的位数
  while (node !== null && bits > 0) {
    // 判断这一位是0 还是 1
    if (!(bits & k)) {
      // 0就往左走
      node = node.left;
    } else {
      // 1就往右走
      node = node.right;
    }
    // 然后右移一位,去拿下一位
    bits >>= 1;
  }

  // 如果 node 最后不为空,那么这个节点就是存在的
  return node !== null;
}

var countNodes = function (root) {
  if (root === null) {
    return 0;
  }

  // 层数
  let level = 0;
  let node = root;

  // 因为是完全二叉树,所以一直往左走,就是最大层数
  while (node.left !== null) {
    level++;
    node = node.left;
  }

  // 第 level 层编号最小是 2的level次方,最大是2的(level+1)次方 - 1
  let low = 1 << level, 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;
};

✏️ 方法二:

解题关键词:二叉树递归

解题思路:递归判断一颗树是不是满二叉树,如果是二叉树,直接运用公式返回节点数量,如果不是二叉树,使用递归分别判断左右子树,最后加和。 <br /

typescript
function countNodes(root: TreeNode | null): number {
  // 如果当前节点为空,返回0
  if (root === null) return 0;

  // 左右子树
  let left = root.left, right = root.right;
  // 左右子树深度
  let leftDepth = 0, rightDepth = 0;

  // 计算最左边深度
  while (left) {
    leftDepth += 1
    left = left.left
  }
  // 计算最右边深度
  while (right) {
    rightDepth += 1
    right = right.right
  }

  // 如果两边的深度相同,说明是一颗满二叉树,那么直接使用公式
  // 因为当前节点是根节点,也占用一层,所以要 depth + 1
  if (leftDepth === rightDepth) return 2 ** (leftDepth + 1) - 1

  // 不是满二叉树,使用递归分别计算左右子树节点数量,并且加上当前节点的数量
  return 1 + countNodes(root.left) + countNodes(root.right)
};