【问题标题】:Binary Tree Nodes - Which Way?二叉树节点 - 哪种方式?
【发布时间】:2009-11-19 23:27:54
【问题描述】:

对于我们在数据结构中给出的任务,我们必须创建一个测试类来确定我们得到的代码是否正确遍历了测试类中的二叉树。

这些是提供给我们的 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);

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

【问题讨论】:

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


    【解决方案1】:

    第二个使用字符串作为“元素”值,第一个使用 BinaryTreeNode。我不知道“元素”的值应该是什么,但我的直觉说它应该是一个字符串。这是这些 sn-ps 之间的唯一区别。

    【讨论】:

      【解决方案2】:

      好坏参半 ;-)

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

      至少,我建议将第一个 sn-p 重写为

      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)。这不是一个大问题,因为这些变量是引用类型,因此需要很少的存储和非常有效的复制。第二种方法本质上是在原位构建这些节点,这可以被认为更有效。 (也就是说,随着树的深度增加,试图以这种方式创建所有中间节点很快就会变得不可行......)

      出现了一个有趣的停顿,一个教学时刻 ;-),我们反思premature optimization...的诱惑...

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

      这就是说,

      这两种方法都不是 Supercalifragilisticexpialidocious。

      一种更酷的方法可能是从文本文件中读取树。该文本中的语法可能非常接近方法#1的布局中明显的每行一个命名节点,因此有人可能会争辩说它本身不会有很大的收获......也许只是节省几次击键。然而,当我们考虑到 解耦“数据”与“代码”时,这种解决方案的优势变得更加明显。我们不需要重新编译程序(或有无数不同版本的这个二进制文件)要在不同的树上工作,我们只需将程序指向不同的文本文件。当/如果代码被重构时,这种解耦变得有趣,比如节点类的名称被修改。在极端情况下,此代码与数据独立性将允许使用完全不同的树库,唯一必要的更改是在从文件中解析节点的逻辑结构后生成一些映射节点构造和组装的逻辑。

      【讨论】:

      • 我可能会从文本文件中读取,但我们被特别要求对树进行硬编码,所以很遗憾,这已经失效了。不过我确实喜欢你的重写,这是一个很好的中间人。
      • 使用数组是否符合“硬编码”条件?如果是这样,您将获得数据化树定义的优势,而无需解析输入文件。您仍然可以保留多个版本的树(只是几个具有不同名称的数组,或者可能是一个大数组中的额外维度),而不是一个不同的文件名,您将收到一个不同的“排序索引”指定要构建的 树。然后,再一次,您可能会坚持真正的硬编码,在这种情况下,方法#1 将是我的选择。
      • @StormPooper,请参阅我对可能的“混合”解决方案的编辑,其中底部 2 层使用方法 #2 处理(即每行代码最多 3 个节点),树的其余部分使用方法 1:两全其美:更紧凑但更规则的结构。
      【解决方案3】:

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

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

      布丁的证据在吃:你想怎么处理这样一棵树?在第二种情况下,您从根节点开始,读取它的元素,如果它是一个运算符,则在适当的情况下递归读取子节点,然后对其值执行运算符的功能。然而,在第一种情况下,您不读取元素而是首先检查其类型——如果它是对另一个树节点的引用,则意味着它是一个运算符,因此您将该节点的值作为运算符。这个结论“它是一个树节点,所以它是一个运算符”对你来说应该听起来很可疑。如果你想对类型进行dipatching,类型应该是有意义的,所以操作符应该是“操作符”类型。不过,对于这个练习,我认为对字符串值进行分派就足够了。结论:将运算符直接放入节点中。

      【讨论】:

        【解决方案4】:

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

        【讨论】:

        • 在上面的第一个例子中,元素很明显是BinaryTreeNodes。在第二个例子中,它们不是。因此,第二个例子更好。
        【解决方案5】:

        第二个读起来更好。如果您熟悉反向波兰符号,则第二个读起来就像您将输入到 RPN 计算器中一样。我认为这里的效率不应该是一个真正的问题,因为这只是一个验证类功能的测试。

        【讨论】:

          【解决方案6】:

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-11-25
            • 2012-04-15
            • 2010-11-22
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多