【问题标题】:Java Implementing Functions On A TreeJava 在树上实现函数
【发布时间】:2015-09-30 09:43:05
【问题描述】:

我正在解决一个问题,要求我为给定的接口实现多个功能。

我有一个接口是树中的单个节点,还有一个接口是树。

树节点

package main;

import java.util.List;

public interface TreeNode<E> {
    public E getElement();
    public void setElement(E elem);
    public void setChild(TreeNode<E> child);
    public TreeNode<E> getFirstChild();
    public List<TreeNode<E>> getChildren();
    public void setNextSibling(TreeNode<E> sibling);
    public TreeNode<E> getNextSibling();
    public int size();
    public int height();
    public List<TreeNode<E>> getPreOrder();
    public List<TreeNode<E>> getPostOrder();
    public String toString(TreeNode<E> node);
}

package main;

import java.util.List;

public interface Tree<E> {
    public TreeNode<E> getRoot();
    public int size();
    public int height();
    public List<TreeNode<E>> getPreOrder();
    public List<TreeNode<E>> getPostOrder();
    public void makeEmpty();
    public boolean isEmpty();
    public int height(TreeNode<E> node);
    public int depth(TreeNode<E> node);
}

我被要求:

使用 last-child/previous-sibling 实现这些接口 方法。创建将扩展 Tree 和 TreeNode 的 MyTreeNode.java 和 MyTree.java。

MyTreeNode.java 定义为:

扩展 TreeNode.java 的实现。

MyTree.java 定义为:

扩展 Tree.java 的实现。

于是我开始构建MyTree.java。问题是,我不知道如何“扩展”接口和实现方法。对我来说,这听起来像是希望我实现接口并构造已定义的方法。据我所知,只有一个接口可以扩展另一个接口。

另一个问题是,如果没有适当的左右节点,我将如何创建许多这样的函数?我相信我不允许修改现有的接口(Tree.javaTreeNode.java)。

谁能帮我用通俗的话向我解释我应该做什么?

【问题讨论】:

    标签: java data-structures tree elements


    【解决方案1】:

    你的班级需要implement the interface,比如:

    public class MyTree<E> implements Tree<E> {
       // implement the methods of interface.
    }
    

    【讨论】:

    • 谢谢。这就是我的想法。您能否推荐如何创建一个变量来保存根节点和左右子节点?我不确定这一点,因为界面不包括这些。
    【解决方案2】:
    1. 在java中,你只能扩展一个类,实现一个接口。你是对的,只有接口可以扩展另一个接口。我认为问题是要创建一个实现接口的类。

    2. 在您的实现中,您将定义正确的左右节点。因此,在您实现的每一个具体功能中,您都可以直接引用这些字段。

    【讨论】:

      【解决方案3】:

      回答您关于术语的问题:

      是的,您是对的,您的讲师/助教根本没有准确地编写它(可能他们更多来自 C++ 背景,接口/基类和扩展/实现之间没有严格区分)。我相信您的解释正是他们想要的。

      由于问题明确提到使用“last-child, prev兄弟”(与接口中的“first-child, next-sibling”相反),我相信你不应该使用left/right来实现(实际上意味着同时存储上一个和下一个兄弟,这似乎违背了练习的目的)。我认为他们只是让问题变得棘手而不现实,但找到方法很有趣。

      不存储“下一个兄弟”的一种方法是在节点中存储和额外属性,即父节点。 “First-child,Next-Sibling”和“Last-Child,Prev-Sibling”其实是同一个东西,意思是表示

         A
         |
         V
      [B C D] 
      

      第一个孩子,下一个兄弟正在存储:

         A
         |
         V
         B -> C ->D
      

      Last Child,Previous Sibling,如果我猜对了,就是存储

         A
         +--------+
                  V
         B <- C <-D       (and B,C,D is storing a parent ref pointing to A)
      

      所以,为了实现TreeNode.firstChild(),你只需要转到它的lastChild(),并继续遍历prevSibling,直到你找到一个没有prevSibling的节点(即没有前一个兄弟节点的节点意味着它是儿童中的第一个)。

      同样,要实现TreeNode.nextSibling(),您需要转到其parentlastChild,并继续查找prevSibling 等于this 的节点(即找到以自己为前一个兄弟的节点,这意味着该节点是我的下一个兄弟节点)。


      一些伪代码:

      /** Tree node implementation using "last-child-prev-sibling" */
      MyTreeNode<E> implements TreeNode<E> {
          MyTreeNode<E> parent;
          MyTreeNode<E> lastChild;
          MyTreeNode<E> prevSibling;
      
          // I bet it is add child, which add a new child to the last
          public void setChild(TreeNode<E> child) {
              if (! (child instanceof MyTreeNode)) {
                  throw new SomeKindOfImpatibleTypeException();
              }
              MyTreeNode<E> newLastChild = (MyTreeNode<E>) child;
              newLastChild.parent = this;
              newLastChild.previousChild = this.lastChild;
              this.lastChild = newLastChild;
          }
      
          public TreeNode<E> getNextSibling() {
              if (this.parent == null) {
                  return null;
              }
              MyTreeNode<E> n = this.parent.lastChild;
              if (n == this) {
                  return null;
              }
              for ( ; n != null; n = n.prevSibling) {
                  if (n.prevSibling == this) {
                      return n;
                  }
              }
              return null;
          }
      
          public TreeNode<E> firstChild() {
              if (lastChild == null) {
                   return null;
              }
              for (TreeNode<E> n = lastNode; 
                   n.prevSibling != null; 
                   n=n.prevSibling) {
              }
              return n;
          }
      }
      

      只是告诉你如何实现 2 个关键方法,另一个留给你自己

      【讨论】:

      • 非常感谢您解答这个问题。我的教授有时会这样含糊其辞。我将如何准确地实现这些功能?由于没有预定义的根或左/右孩子,我不知道下一步该去哪里。
      • 我已经为您提供了 2 个关键方法的逻辑。如果您有左右/上一个孩子,那将非常容易,但我怀疑这是否是您的教授正在寻找的。让我更新一些代码
      • 我已经更新了我的代码以适应你的伪代码。这是我当前代码的粘贴箱:pastebin.com/q9yLJXxM 我现在正在做的事情是前序和后序遍历。但是,如果没有左右孩子,我不确定如何做到这一点。对不起,我不完全理解,我是新手。谢谢。
      • 无法在工作中看到您的 pastebin 代码。但是,如果您实施了getFirstChild()getNextSibling(),则后序/前序遍历应该是直截了当的。您的遍历代码可以简单地利用这些方法。例如preOrderTraverse(){ resultList += this; for (n = firstChild(); n != null; n = n.getNextSibling()) { resultList+= n.preOrderTraverse(); }`
      • 我还没有实现getFirstChild()。我现在拥有的两个数据成员是lastChildprevSibling,我看不出它们对getFirstChild() 有何帮助。不过我可能弄错了。我需要一些方法来访问下一个元素,而不是同一行(兄弟)上的元素,但我没有这些元素的数据成员。我会为此目的创建一个吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      • 2015-01-21
      • 1970-01-01
      • 1970-01-01
      • 2014-10-26
      相关资源
      最近更新 更多