计算完整二叉树中的节点数

时间:2015-07-01 22:47:26

标签: algorithm recursion tree binary-tree

我想计算Complete Binary tree中的节点数,但我能想到的只是遍历整个树。这将是一个O(n)算法,其中n是树中节点的数量。什么是最有效的算法来实现这个目标?

4 个答案:

答案 0 :(得分:8)

假设我们从树的左右脊上走下来确定它们的高度。我们要么发现它们是相同的,在这种情况下最后一行是满的,否则我们会发现它们是不同的。如果高度返回相同(比如高度为h),那么我们知道有2个 h - 1个节点,我们就完成了。

否则,高度必须分别为h + 1和h。我们知道至少有2个 h - 1个节点,加上树底层的节点数。那么,问题是如何解决这个问题。一种方法是在最后一层找到最右边的节点。如果您知道该节点在哪个索引处,您确切知道最后一层中有多少节点,那么您可以将其添加到2 h - 1并且您已完成。

如果你有一个左侧高度为h + 1的完整二叉树,则有1到2个 h - 1个可能位于最后一层的节点。问题是如何尽可能有效地确定这一点。

幸运的是,由于我们知道最后一层中的节点从左到右填充,我们可以使用二进制搜索来尝试找出最后一层中最后一个填充节点的位置。从本质上讲,我们猜测它可能是哪个索引,从树的根部走到那个叶子的位置,然后在那里找到一个节点(所以我们知道底层最右边的节点是那个节点或在右边)或我们没有(所以我们知道底层最右边的节点必须纯粹在当前位置的右边)。我们可以通过使用数字k中的位来向下走到底层第k个节点的位置来引导搜索:我们从根开始,然后如果k的第一位为0则向左移动,如果是k的第一位是1,然后以相应的方式使用剩余的位来向下走树。我们这样做的总次数是O(h),每次探测花费时间O(h),所以这里完成的总工作是O(h 2 )。由于h是树的高度,我们知道h = O(log n),因此该算法需要时间O(log 2 n)才能完成。

我不确定是否可以改进此算法。但是,我可以在任何正确的算法上获得Ω(log n)下限。我将争辩说,在所有情况下始终正确的任何算法都必须检查树的最后一行中最右边的叶节点。要知道原因,假设有一个树T,算法不会这样做。假设算法在底行检查的最右边的节点是x,底行中实际最右边的节点是y,而算法检测到的最下面一行中最左边的丢失节点是z。我们知道x必须在y的左边(因为算法没有检查底行中最左边的节点)并且y必须在z的左边(因为y存在而z不存在,所以z必须比y)更靠右边。如果你考虑算法的“知识”在这一点上是什么,算法不知道是否有任何节点纯粹在x的右边或纯粹在z的左边。因此,如果我们给它一个修改后的树T',我们删除了y,算法就不会注意到任何东西已经改变了,并且在T和T'上会有完全相同的执行路径。然而,由于T和T'具有不同数量的节点,因此算法必须在其中至少一个上是错误的。由于走下树所需的时间,检查此节点至少花费时间Ω(log n)。

简而言之,你可以使用上面的O(log 2 n)-time算法比O(n)做得更好,你可能做得更好,尽管我我不完全确定如何或是否可能。我怀疑这不是因为我怀疑二进制搜索是检查底行和路径长度到探测节点的最佳方式,即使考虑到它们共享共同的节点,也是Θ (log 2 n),但我不确定如何证明它。

希望这有帮助!

答案 1 :(得分:1)

public int leftHeight(TreeNode root){
    int h=0;
    while(root!=null){
        root=root.left;
        h++;
    }
    return h;
}
 public int rightHeight(TreeNode root){
    int h=0;
    while(root!=null){
        root=root.right;
        h++;
    }
    return h;
}
public int countNodes(TreeNode root) {
    if(root==null)
        return 0;
    int lh=leftHeight(root);
    int rh=rightHeight(root);
    if(lh==rh)
        return (1<<lh)-1;
    return countNodes(root.left)+countNodes(root.right)+1;

}

在每次递归调用中,我们需要遍历完整二叉树的左右边界来计算左右高度。如果它们相等,那么树就满了2 ^ h-1个节点。否则我们会在左子树和右子树上进行递归。第一个调用来自根(level = 0),需要花费O(h)时间来获得左右高度。我们有一个递归,直到我们得到一个完整二叉树的子树。在最坏的情况下,我们可能会发生这种情况直到叶节点。所以复杂度将是(h +(h-1)+(h-2)+ ... + 0)=(h(h + 1)/ 2)= O(h ^ 2)。空间复杂度也是大小调用堆栈,即O(h)。 注意:对于完整的二叉树树h = log(n)。

答案 2 :(得分:0)

如果二叉树肯定是完整的(而不是维基百科文章中定义的“接近完成”或“几乎完成”),您应该简单地将树的一个分支下降到叶子。这将是O(logn)。然后将两个的力量加总到这个深度。所以2 ^ 0 + 2 ^ 1 ... + 2 ^ d

答案 3 :(得分:0)

C#Sample可能会帮助其他人。这类似于上面templatetypedef

所解释的时间复杂度
public int GetLeftHeight(TreeNode treeNode)
{
    int heightCnt = 0;

    while (treeNode != null)
    {
        heightCnt++;
        treeNode = treeNode.LeftNode;
    }
    return heightCnt;
}

public int CountNodes(TreeNode treeNode)
{
    int heightIndx = GetLeftHeight(treeNode);

    int nodeCnt = 0;

    while (treeNode != null)
    {
        int rightHeight = GetLeftHeight(treeNode.RightNode);

        nodeCnt += (int)Math.Pow(2, rightHeight); //(1 << rh);

        treeNode = (rightHeight == heightIndx - 1) ? treeNode.RightNode : treeNode.LeftNode;

        heightIndx--;
    }

    return nodeCnt;
}