二叉树节点 - 哪种方式?

时间:2009-11-19 23:27:55

标签: java data-structures binary-tree tree-traversal

对于我们在Data Structures中给出的赋值,我们必须创建一个测试类来确定我们给出的代码是否正确遍历了测试类中的二叉树。

这是给我们的BinaryTreeNode类的3个构造函数:

public BinaryTreeNode(Object theElement, BinaryTreeNode theleftChild, BinaryTreeNode therightChild)
{
    element = theElement;
    leftChild = theleftChild;
    rightChild = therightChild;
}

public BinaryTreeNode(Object theElement)
{
    element = theElement;
}

public BinaryTreeNode() {} 

我在测试类中快速创建了以下内容,以创建指定的树之一:

// tree for ( A - B ) / C
BinaryTreeNode b1 = new BinaryTreeNode("A");
BinaryTreeNode b2 = new BinaryTreeNode("-");
BinaryTreeNode b3 = new BinaryTreeNode("B");
BinaryTreeNode b4 = new BinaryTreeNode("/");
BinaryTreeNode b5 = new BinaryTreeNode("C");
BinaryTreeNode bAB = new BinaryTreeNode(b2, b1, b3);
BinaryTreeNode bRoot = new BinaryTreeNode(b4, bAB, b5);
q.put(bRoot);

然而,我的朋友建议我这样做:

// tree for ( A - B ) / C
BinaryTreeNode b1 = new BinaryTreeNode("A");
BinaryTreeNode b2 = new BinaryTreeNode("B");
BinaryTreeNode b3 = new BinaryTreeNode("C");
BinaryTreeNode bRoot= new BinaryTreeNode("/", new BinaryTreeNode("-", b1, b2), b3);
q.put(bRoot);

但是,他很难解释为什么这种方式更好。有谁可以解释为什么这更有效率?如果示例中需要更多代码,请询问。

6 个答案:

答案 0 :(得分:3)

第二个使用字符串作为'element'值,第一个使用BinaryTreeNode。我不知道什么应该是'元素'的价值,但我的直觉说它应该是一个字符串。这是这些片段之间的唯一区别。

答案 1 :(得分:2)

这是一个混合包; - )

第二种方法在产出方面似乎更好:
始终 将字符串放在BinaryTreeNode的元素成员中,从而第一种方法为此目的使用混合字符串和BinaryTreeNode实例。

至少,我建议将第一个片段重写为

BinaryTreeNode b1 = new BinaryTreeNode("A");
BinaryTreeNode b3 = new BinaryTreeNode("B");
BinaryTreeNode b5 = new BinaryTreeNode("C");
BinaryTreeNode bAB = new BinaryTreeNode("-", b1, b3);
BinaryTreeNode bRoot = new BinaryTreeNode("/", bAB, b5);

第二种方法的另一个优点是它不需要为中间节点额外存储(bAB,例如上面)。这不是一个大问题,因为这些变量是引用类型,因此非常有效地需要很少的存储和复制。第二种方法基本上构建这些节点 in-situ ,这可以被认为更有效。 (这就是说,尝试以这种方式创建所有中间节点随着树的深度增长而变得很快变得不可行......)

有一个有趣的停顿,一个教学时刻;-),我们在那里反思premature optimization ...

的诱惑

另一方面......第一种方法(指示语义变化)的一个优点是,它使更多常规代码的支出,这可能有助于更容易地找到树结构中的问题 [编辑:] 可能的混合解决方案是使用解决方案#2来定义树的底部两层(叶子节点和它们上面的层),即定义[up to]每行三个节点。这样做的好处是可以大致减少定义树所需的行数(可能会随着树的增长而成为一个因素),也就是不必为最多的节点找出并引用变量(叶子 - 节点)。现在......这样的结构/语法,当/如果树被附加到,需要重新平衡它等时灵活性会低得多(也许这就是为什么教师建议对树进行硬编码,给出学生对重新平衡树所需的操作类型有“感觉”。)

这就是说,

这两种方法都不是Supercalifragilisticexpialidocious。

稍微冷静的方法可能是从文本文件中读取树。该文本中的语法可能反映了方法#1布局中明显的每个命名节点的线,因此有人可能认为它本身并不是一个很大的收获...也许只是保存一些按键。然而,当我们考虑这个从“代码”中解除“数据”时,这种解决方案的优势变得更加明显。我们不需要重新编译程序(或者使用这个二进制文件的不同版本)在不同的树上工作,我们只是将程序指向不同的文本文件。当代码被重构时,例如修改了节点类的名称,这种去耦也变得很有趣。在极端情况下,此代码与数据独立性将允许使用完全不同的树库,唯一必要的变化是产生一些逻辑,该逻辑在从文件解析节点的逻辑结构之后映射节点构造和汇编。

答案 2 :(得分:1)

左边是第一个代码生成的结构,第二个代码中的一个是右边的结构:

          * -- "/"            "/"
         / \                  / \
        /   \                /   \
"-" -- *    "C"            "-"   "C"
      / \                  / \
     /   \                /   \
   "A"   "B"            "A"   "B"

布丁的证据在于吃:你想怎样处理这样一棵树?在第二种情况下,从根节点开始,读取其元素,如果它是运算符,则根据需要递归读取子元素,然后对其值执行运算符函数。但是,在第一种情况下,您不会读取元素,而是先检查其类型 - 如果它是对另一个树节点的引用,则表示它是一个运算符,因此您将该节点的值作为运算符。这个结论“它是一个树节点,所以它是一个操作员”应该听起来很腥。如果你想对类型进行dipatching,那么类型应该有意义,因此运算符应该是“operator”类型。但是,对于本练习,我认为调度字符串值就足够了。总之:将运算符直接放入节点中。

答案 3 :(得分:0)

每个节点的元素不需要是BinaryTreeNode本身。任何任意对象都可以。

答案 4 :(得分:0)

第二个读取更好的一件事。如果你熟悉反向抛光表示法,那么第二个就像你输入RPN计算器一样。我认为效率不应该成为一个问题,因为这只是一个验证类功能的测试。

答案 5 :(得分:0)

我是朋友,我的理由是将元素作为节点是不合适的,因为在这种情况下我们用它来表示数学方程式。我的理解是,您可以为引用为节点元素的单独树创建根节点,但这对于此示例完全不合适。

我在第一种方式中看到的错误是,当他使用变量bAB(即bAB中的节点b1,b2和b3)时,他试图存储整个子树。我觉得这会在工作时忽略这一点用树来表示数学方程式。