当前位置:   article > 正文

222.完全二叉树的节点个数 |递归优化思路 + 复杂度分析_满二叉树时间复杂度

满二叉树时间复杂度

完全二叉树的节点个数

leetcode : https://leetcode.cn/problems/count-complete-tree-nodes/

递归思路

递归的思路很简单, 假设们要统计一棵树的节点数, 那么 只要统计根节点的左子树的节点数, 和右子树的节点数加上根节点即可

image-20230112200228510

那么, 假设我们要统计左子树的节点数, 其实就变成了统计以2为根节点的树的节点个数

image-20230112200507426

进一步分解, 假设我们要统计上面树的节点数, 就变成了统计左子树 4 和右子树 5 的两棵子树的节点数的子问题, 这个问题逐渐分解, 分解到最小单位

image-20230112200723197

统计上面这样一棵树的节点个数, 这棵树同样是需要统计左子树的节点数 + 右子树的节点数 + 1(root)

思考 : 假设一个大的计算整棵树的大问题, 可以像现在这样一步一步的分解成一个个子问题的情况下, 我们就可以使用递归

递归流程

下面的流程是计算普通二叉树的流程 :

1.确定函数参数以及返回值
  • 这个函数的目的是为了计算一棵树的总的节点数, 所以参数为 count(TreeNode root)
  • 返回值为 int , 也就是树的节点数
  • private int count(TreeNode root)
2.终止条件

我们在设置终止条件时, 一定要将问题分解到最小时, 去考虑终止条件, 一定不能在大问题上去考虑, 就比如这道题,

image-20230112201404213
  1. 左右节点都为 NULL , 直接返回 1 , 因为只有root节点存在
3.当前层逻辑

当前层也就是左右节点至少有一个存在, 需要统计左右, 或者某一个

  1. 左节点存在, 右节点为NULL , 此时就要去统计 左子树的节点数 + 1 (root) : 1 + count(root.left)
  2. 左节点为NULL, 右节点存在 (本题是二叉树, 不存在这样的情况, 但是我们暂时也把这种情况放进去, 适用于所有的树) : 1 + count(root.right)
  3. 左节点都存在, 则需要统计左子树 和 右子树节点数

实际上本题是完全二叉树, 满二叉树的计算公式是假设层数是h, 节点数是 2 h − 1 2^h - 1 2h1 , 只需要让指针分别从左右两边遍历计算深度, 判断是否一致,

如果是满二叉树, 直接使用公式计算, 如果不是, 按照普通二叉树计算即可

递归代码

1.代码实现
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int countNodes(TreeNode root) {
        
        if(root == null) {
            return 0;
        }

        return count(root);
    }


    private int count(TreeNode root) {

        // 处理全空的情况
        if(root.left == null && root.right == null) {
            return 1;
        }

        // 处理只有一个节点为空
        if(root.left == null && root.right != null) {
            return 1 + count(root.right);
        }

        if(root.left != null && root.right == null) {
            return 1 + count(root.left);
        }

        // 左右都不为空
        int leftCount = count(root.left);
        int rightCount = count(root.right);

        // 左右子树的节点数量 + 本身的
        return leftCount + rightCount + 1;

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

上面的代码是为了便于理解, 如果想要简化的话, 很简单 :

class Solution {
    public int countNodes(TreeNode root) {
         if (root == null) return 0;
    return 1 + countNodes(root.left) + countNodes(root.right);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
2.优化版

优化版是考虑了满二叉树, 先判断从左边遍历和从右边遍历的层高是否相同, 如果相同则可以使用 公式直接计算, 不同再使用普通方法计算

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int countNodes(TreeNode root) {
        
        TreeNode le = root, ri = root;
        // 判断是否是完全二叉树
        int hl = 0, hr = 0;
        
        while(le != null) {
            le = le.left;
            hl++;
        }

        while(ri != null) {
            ri = ri.right;
            hr++;
        }

        if(hl == hr) {
            return (int)Math.pow(2, hl) -1;
        }

        return 1 + countNodes(root.left) + countNodes(root.right);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
3.时间复杂度分析

计算普通树递归的时间复杂度是 O ( n ) O(n) O(n) , 因为其实是访问了所有的节点, 计算满二叉树的时间复杂度是层高 O ( l o g N ) O(log_{N}) O(logN) , N N N 是节点的个数, h h h 是层高
N = 2 h − 1 = = > h = l o g N + 1 N = 2^h - 1 ==> h = log_{N+1} \\ N=2h1==>h=logN+1
但是实际上的时间复杂度是 O ( l o g N ∗ l o g N ) O(log_{N} * log_{N}) O(logNlogN) , 因为最坏情况一定有一半是满二叉树. 另一半是完全二叉树

  • 第一层, root

  • 第二层, 有两棵树, 右边是满二叉树 : l o g N logN logN, 左边不确定

image-20230112215012864

  • 第三层, 右边满二叉树 : l o g N logN logN , 左边不确定
image-20230112214416355
  • 第四层, 右边满二叉树 : l o g N logN logN , 左边不确定
image-20230112214453525
  • 第五层, 也就是最后一层, 都是完全二叉树
image-20230112214703431

层高是 O ( l o g N ) O(logN) O(logN) , 每一层需要计算的时间复杂度是 O ( l o g N ) O(logN) O(logN) , 所以实际上的时间复杂度是 O ( l o g N ∗ l o g N ) O(logN * logN) O(logNlogN)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/398839
推荐阅读
相关标签
  

闽ICP备14008679号