在上一篇中,我们学习了解了平衡二叉树,并且利用DFS进行了验证。在本节中,我们将继续学习完全二叉树的相关内容。首先了解一下什么是完全二叉树。
01
完全二叉树
完全二叉树由满二叉树引出,先来了解一下什么是满二叉树:
如果二叉树中除了叶子结点,每个结点的度都为 2,则此二叉树称为满二叉树。**(二叉树的度代表某个结点的孩子或者说直接后继的个数。**对于二叉树而言,1度是只有一个孩子或者说单子树,2度是有两个孩子或者说左右子树都有。)
比如下面这颗:
那什么又是完全二叉树呢:
如果二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,则此二叉树被称为完全二叉树。
比如下面这颗:
而这颗就不是:
熟悉了概念,我们来看题目:
02
第222题:完全二叉树的节点个数
第222题:给出一个完全二叉树,求出该树的节点个数。 说明:
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
示例:
输入:
1
/ \
2 3
/ \ /
4 5 6
输出: 6
03
递归求解
首先分析题目,我们很容易可以想到通过递归,来求解节点个数。
1func countNodes(root *TreeNode) int {
2 if root != nil {
3 return 0
4
5 }
6 return 1 + countNodes(root.Right) + countNodes(root.Left)
7}
但是很明显,出题者肯定不是要这种答案。因为这种答案和完全二叉树一毛钱关系都没有。所以我们继续思考。
04
经典解法
11563SO6_0.png 由于题中已经告诉我们这是一颗完全二叉树,我们又已知了完全二叉树除了最后一层,其他层都是满的,并且最后一层的节点全部靠向了左边。那我们可以想到,可以将该完全二叉树可以分割成若干满二叉树和完全二叉树,满二叉树直接根据层高h计算出节点为2^h-1,然后继续计算子树中完全二叉树节点。那如何分割成若干满二叉树和完全二叉树呢?对任意一个子树,遍历其左子树层高left,右子树层高right,相等左子树则是满二叉树,否则右子树是满二叉树。这里可能不容易理解,我们看图。
假如我们有树如下:
我们看到根节点的左右子树高度都为3,那么说明左子树是一颗满二叉树。因为节点已经填充到右子树了,左子树必定已经填满了。所以左子树的节点总数我们可以直接得到,是2^left - 1,加上当前这个root节点,则正好是2^3,即 8。然后只需要再对右子树进行递归统计即可。
那假如我们的树是这样:
我们看到左子树高度为3,右子树高度为2。说明此时最后一层不满,但倒数第二层已经满了,可以直接得到右子树的节点个数。同理,右子树节点+root节点,总数为2^right,即2^2。再对左子树进行递归查找。
根据分析,得出代码:
1//java
2class Solution {
3 public int countNodes(TreeNode root) {
4 if (root == null) {
5 return 0;
6 }
7 int left = countLevel(root.left);
8 int right = countLevel(root.right);
9 if (left == right) {
10 return countNodes(root.right) + (1 << left);
11 } else {
12 return countNodes(root.left) + (1 << right);
13 }
14 }
15
16 private int countLevel(TreeNode root) {
17 int level = 0;
18 while (root != null) {
19 level++;
20 root = root.left;
21 }
22 return level;
23 }
24}
学会请留言打卡!
让我看到你的努力~
注:本系列所有教程中都不会用到复杂的语言特性,大家不需要担心没有学过语法知识。算法思想最重要,使用各语言纯属本人爱好。同时,本系列所有代码均在leetcode上进行过测试运行,保证其严谨性!