大家好,我是挨打的阿木木,爱好算法的前端摸鱼老。最近会频繁给大家分享我刷算法题过程中的思路和心得。如果你也是想提高逼格的摸鱼老,欢迎关注我,一起学习。
题目
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) 的简单解决方案。你可以设计一个更快的算法吗?
思路
- 常规思路是写一个递归方法,去递归树的每一个节点,做一个数量的统计;
- 但是题目中已经指定了完全二叉树,完全二叉树的概念是,从上到下每个空位都填满,从左到右每个空位都填满,这意味着如果有空位,只会出现在最后一行的最右边;
- 这就给了我们很大的操作空间,我们可以判断数有几层去算出全部放满的话有多少个节点,记为
total,同时我们可以用一个变量base去记录当前这一级放满的话有多少个节点,每经过一轮base的数量翻倍; - 然后用二分查找,每次找当前这一级的中间节点是否存在,如果存在说明左子树全部填满了,否则说明右子树全部空着,因为完全二叉树是从左到右排放的。具体实现是:每次找左子树的最右边一个节点,若是存在说明左子树全部没有空位,这时候分界点在右子树身上,递归继续找右子树即可。不存在说明右子树全是空位,这时候分界点在左子树身上,这种情况下递归找之前还有减去右子树所有的空位, 也就是我们的
base / 2; - 这样子就形成了递归问题,每次树的根节点变成了它的左节点或者右节点,直到最后一轮经过最后一个节点,单独判断节点是否存在即可。
常规实现
/**
* @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;
}
结果
看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。