222. Count Complete Tree Nodes

Given a complete binary tree, count the number of nodes.

Definition of a complete binary tree from Wikipedia:
In a complete binary tree every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible. It can have between 1 and 2h nodes inclusive at the last level h.

计算一个完全二叉树的结点数量,关于完全二叉树的定义,维基百科上是这样写的,

In a complete binary tree every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible. It can have between 1 and 2h-1 nodes at the last level h.[19] An alternative definition is a perfect tree whose rightmost leaves (perhaps all) have been removed. Some authors use the term complete to refer instead to a perfect binary tree as defined above, in which case they call this type of tree an almost complete binary tree or nearly complete binary tree.[20][21] A complete binary tree can be efficiently represented using an array.[19]

一个二叉树,除了最后一层外,其它层的每个结点均具有两个孩子结点,最后一层的结点是从左往右排列的,这样的二叉树就是一个完全二叉树。

这个问题的难点在于如何找到最后一层有多少个结点,我们可以计算出树的高度为h,那么总结点数量=2^(h - 1) - 1 + 最后一层结点数。想要到最后一层,必须从根结点往下找,那么比较直接的一种想法,就是直接二叉树,求出所有的结点,这样子虽然比较简单,但是显然不是这个题的目的,要不然给出一个complete binary tree就没有意义了。

姑且将最后一层的从左往右的最后一个点称为断点。

首先我们先将数的深度求出h

要找到断点的位置,并且不是遍历树,则不能是层遍历,只能是深度遍历,最容易想到的是从根结点开始,按照前序的顺序,沿着一条路径到根结点,并查看这个根结点的深度是否为h,如果它的深度为h,则说明它不是断点,非断点个数count加一,如果它的深度为h - 1,则说明它是一个断点,这个时候计算结果2^(h - 1) - 1 + count,返回就完成了。

代码如下:(但是这道题扣时间太紧了,导致这个方法直接TLE)

int countNodes(TreeNode* root)
    {
        if (!root)return 0;
        stack<TreeNode*> stk;
        stack<int> stk_int;
        int layer = 0, cur_layer = 1, last_layer_count = 0;
        TreeNode* cur = root;
        while (true)
        {
            while (cur)
            {
                if (cur->right) {
                    stk.push(cur->right); 
                    stk_int.push(cur_layer + 1);
                }
                cur = cur->left;
                cur_layer++;
            }
            cur_layer--;
            if (cur_layer < layer)break;
            if (cur_layer > layer)layer = cur_layer;
            last_layer_count++;
            if (stk.empty())break;
            cur = stk.top();
            cur_layer = stk_int.top();
            stk.pop(); stk_int.pop();
        }
        return pow(2, layer - 1) - 1 + last_layer_count;
    }

那么这种方法挂了,为什么呢,猜测是因为对于非常接近完美二叉树的测试数据,用时和直接遍历二叉树差不多,平均时间复杂度为O(2^(n - 1))。n为树的深度,显然太大了。

换一种思路,对与根结点来说,这棵树的高度为h,如果说它的左子树的高度为h - 1,右子树的高度也为h - 1,这说明断点在右子树里,那么完全二叉树的结点个数就等于左子树的结点个数+1+右子树的结点个数,也就是2^(h - 1) - 1 + 1 + 右子树的结点个数,同样的,如果右子树的高度为h - 2,那么总结点个数就是2^(h - 2) - 1 + 1 + 左子树的结点个数,这么一想,解决方案就变成了一个递归的解。

int height(TreeNode* root)
    {
        if (!root)return 0;
        return 1 + height(root->left);
    }
    int countNodes(TreeNode* root)
    {
        if (!root)return 0;
        if(!root->left && !root->right)return 1;
        int left_height = height(root->left);
        int right_height = height(root->right);
        if (left_height == right_height)return (1 << left_height) + countNodes(root->right);
        return countNodes(root->left) + (1 << right_height);
    }
时间复杂度为O(log n),n为总结点个数,将n转换为树高度,那么时间复杂度就为O(log 2^n) = O(n),n为树的高度。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值