【问题标题】:Calculate height of a tree计算一棵树的高度
【发布时间】:2010-02-17 08:07:49
【问题描述】:

我正在尝试计算一棵树的高度。我不喜欢下面写的代码。

#include<iostream.h>

struct tree
{
    int data;
    struct tree * left;
    struct tree * right;
};

typedef struct tree tree;

class Tree
{
private:
    int n;
    int data;
    int l,r;
public:
    tree * Root;
    Tree(int x)
    {
        n=x;
        l=0;
        r=0;
        Root=NULL;
    }
    void create();
    int height(tree * Height);

};

void Tree::create()
{
    //Creting the tree structure
} 

int Tree::height(tree * Height)
{
    if(Height->left==NULL && Height->right==NULL)
    {return 0;
    }
    else
    {
        l=height(Height->left);
        r=height(Height->right);

        if (l>r)
        {l=l+1;
        return l;
        }
        else
        {
            r=r+1;
            return r;
        }
    }
}

int main()
{
    Tree A(10);//Initializing 10 node Tree object
    A.create();//Creating a 10 node tree

    cout<<"The height of tree"<<A.height(A.Root);*/

}

它给了我正确的结果。 但是在some posts(googled page) 中建议做一个后序遍历并使用这个高度方法来计算高度。有什么具体原因吗?

【问题讨论】:

  • 提示:代码必须全部缩进至少4个空格,否则板子不识别为代码。
  • 你能发一个链接到页面吗?
  • 呸,我花了几分钟试图找出代码出了什么问题。然后我到达问题的结尾,发现“它给了我正确的结果”的评论。 >.
  • @Sandeep:我刚刚为您修复了缩进/格式。您的编辑取消了整个更改。请确保您保留有用的编辑。

标签: c++ data-structures binary-search-tree


【解决方案1】:

但是后序遍历不正是您正在做的吗?假设left和right都是非空的,你先做height(left),然后height(right),然后在当前节点做一些处理。根据我的说法,这是后序遍历。

但我会这样写:

int Tree::height(tree *node) {
    if (!node) return -1;

    return 1 + max(height(node->left), height(node->right));
}

编辑:根据您定义树高的方式,基本情况(对于空树)应为 0 或 -1。

【讨论】:

  • 所以你认为如果我改变顺序,首先右树hiegnt然后左树高度(递归)结果会不同吗?
  • @Sandeep:更改顺序不会改变结果。更改空节点的高度定义。
  • Sandeep:先做右树然后再做左树仍然是后序。后序意味着您在处理子节点之后(发布)处理当前节点。在这种情况下,后序是唯一的选择,因为如果不先查看子节点的高度,就不可能确定从节点看到的高度。如果您对树遍历顺序感到不安全,请参阅 en.wikipedia.org/wiki/Tree_traversal
  • 谢谢汉斯...在所有文档中,我总是得到遍历左子树。遍历右子树。访问根。所以我一直在我的脑海里,左边是在右边之前遍历的,但除此之外也是正确的。我现在很清楚,因为对后序的唯一要求是在孩子之后遍历孩子(左右顺序无关紧要)
  • 我赞成,但在我看来,递归遍历可能被解释为前序或后序(这取决于当前节点工作在哪里完成),但由于你的函数做了一些在递归调用之前和之后工作,这是另一回事。我认为这些东西是 XML 顺序遍历。
【解决方案2】:

代码在至少有一个节点只有一个子节点的树中会失败:

// code snippet (space condensed for brevity)
int Tree::height(tree * Height) {
    if(Height->left==NULL && Height->right==NULL) { return 0; }
    else {
        l=height(Height->left);
        r=height(Height->right);
//...

如果树有两个节点(根和左或右孩子)调用根上的方法将不满足第一个条件(至少有一个子树非空),它将递归调用两个孩子。其中之一是 null,但它仍然会取消引用 null 指针以执行 if

正确的解决方案是Hans 在此处发布的解决方案。无论如何,你必须选择你的方法不变量是什么:要么你允许参数为空的调用并且你优雅地处理它,要么你要求参数是非空的,并保证你不调用带有空指针的方法.

如果您不控制所有入口点(该方法在您的代码中是公共的),第一种情况会更安全,因为您不能保证外部代码不会传递空指针。如果您可以控制所有入口点,则第二种解决方案(将签名更改为引用,并使其成为tree 类的成员方法)可能更清晰(或不清晰)。

【讨论】:

  • 大卫。我用 3 4 5 6 7 进行了测试,结果为 4。你在哪里看到问题。
  • 假设你有两个节点,Root 和一个在右边。您在Root 中调用高度,因为right 不为空,它进入else 分支,该分支将调用l=height(Height-&gt;left);。该递归调用接收一个空指针并尝试在 if 中取消引用它以检查 Height-&gt;left 是否为空。取消引用空指针(递归调用中的Height 为空)会产生未定义的行为,并且在大多数情况下会导致应用程序死亡。
【解决方案3】:

树的高度不会随着遍历而改变。它保持不变。就是节点的顺序随着遍历的变化而变化。

【讨论】:

    【解决方案4】:

    来自wikipedia的定义。

    预购(深度优先):

    1. 访问根目录。
    2. 遍历左子树。
    3. 遍历右子树。

    有序(对称):

    1. 遍历左子树。
    2. 访问根目录。
    3. 遍历右子树。

    后序:

    1. 遍历左子树。
    2. 遍历右子树。
    3. 访问根目录。

    定义中的“访问”是指“计算节点的高度”。在您的情况下,它是零(左右均为空)或 1 + 孩子的组合高度。

    在您的实现中,遍历顺序无关紧要,它会给出相同的结果。如果没有指向您的来源的链接,说明后订购是首选。

    【讨论】:

    • 我看不出使用这种后序遍历的任何具体原因。如果您不确定,也许用户只是将 postorder 视为默认值?还是他觉得“做一次遍历”有点含糊?
    【解决方案5】:

    这里是答案:

    int Help :: heightTree (node *nodeptr)
    {
        if (!nodeptr)
            return 0;
        else
        {
            return 1 + max (heightTree (nodeptr->left), heightTree (nodeptr->right));
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-08
      • 1970-01-01
      相关资源
      最近更新 更多