[前端]_一起刷leetcode 222. 完全二叉树的节点个数

156 阅读3分钟

大家好,我是挨打的阿木木,爱好算法的前端摸鱼老。最近会频繁给大家分享我刷算法题过程中的思路和心得。如果你也是想提高逼格的摸鱼老,欢迎关注我,一起学习。

题目

222. 完全二叉树的节点个数

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。

完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

 

示例 1:

输入: root = [1,2,3,4,5,6]
输出: 6

示例 2:

输入: root = []
输出: 0

示例 3:

输入: root = [1]
输出: 1

 

提示:

  • 树中节点的数目范围是[0, 5 * 104]
  • 0 <= Node.val <= 5 * 104
  • 题目数据保证输入的树是 完全二叉树

 

进阶: 遍历树来统计节点是一种时间复杂度为 O(n) 的简单解决方案。你可以设计一个更快的算法吗?

思路

  1. 常规思路是写一个递归方法,去递归树的每一个节点,做一个数量的统计;
  2. 但是题目中已经指定了完全二叉树,完全二叉树的概念是,从上到下每个空位都填满,从左到右每个空位都填满,这意味着如果有空位,只会出现在最后一行的最右边;
  3. 这就给了我们很大的操作空间,我们可以判断数有几层去算出全部放满的话有多少个节点,记为total,同时我们可以用一个变量base去记录当前这一级放满的话有多少个节点,每经过一轮base的数量翻倍;
  4. 然后用二分查找,每次找当前这一级的中间节点是否存在,如果存在说明左子树全部填满了,否则说明右子树全部空着,因为完全二叉树是从左到右排放的。具体实现是:每次找左子树的最右边一个节点,若是存在说明左子树全部没有空位,这时候分界点在右子树身上,递归继续找右子树即可。不存在说明右子树全是空位,这时候分界点在左子树身上,这种情况下递归找之前还有减去右子树所有的空位, 也就是我们的base / 2
  5. 这样子就形成了递归问题,每次树的根节点变成了它的左节点或者右节点,直到最后一轮经过最后一个节点,单独判断节点是否存在即可。

常规实现

/**
 * @param {TreeNode} root
 * @return {number}
 */
var countNodes = function(root) {
    if (!root) {
        return 0;
    }

    // 当前节点 + 左节点总长度 + 右节点总长度
    return 1 + countNodes(root.left) + countNodes(root.right);

};

优化

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var countNodes = function(root) {
    if (!root) {
        return 0;
    }

    if (!root.left) {
        return 1;
    }

    // 计算如果全都存满了是多少
    let cur = root;
    let total = 1;
    let base = 1;

    while (cur.left) {
        base *= 2;
        cur = cur.left;
        total += base;
    }

    // 找到有多少个不符合条件的需要减掉, base === 2的时候说明到了根节点到左子节点的尽头了。
    // 每一轮我们只需要找左子树的最右一个节点是否存在
    // 若是存在说明左子树全部没有空位,不存在说明右子树全是空位
    while (base > 1) {
        // 每一轮找中间位置即可, 二分查找
        base /= 2;
        if (hasNode(root.left, base)) {
            root = root.right;
        } else {
            root = root.left;
            // 右子树全是空位要减掉
            total -= base;
        }
    }

    // 判断最终节点
    if (!root) {
        total--;
    }

    return total;

};

// 树的二分查找, 判断左子树的最右下角节点是否存在
function hasNode(node, base) {
    while (base > 1) {
        node = node.right;
        base /= 2;
    }
    return !!node;
}

结果

image.png

看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。