【问题标题】:How to fill an array with node elements from a binary search tree, in ascending order?如何用二叉搜索树中的节点元素按升序填充数组?
【发布时间】:2011-03-18 12:02:11
【问题描述】:

在学校作业中,我应该完成一个返回数组的方法 节点元素按升序排列。节点是在二叉搜索树中组装的,所以为了正确排序它们,我得到了一个技巧来创建一个递归方法来完成这项工作。

问题在于,根据测试输出,这甚至不会产生集合中的所有元素(java.lang.AssertionError: toArray() 不会返回集合中的所有元素。)

我想不出任何其他方法来处理数组,我不太确定递归是否有效。任何帮助深表感谢。 以下是我的代码:

public class BinarySearchTree<E extends Comparable<E>> implements
    IfiCollection<E> {

    Node root;
    Node current;
    int size = 0;
    int i = 0;

    public class Node {
    E obj;
    Node left, right;

    public Node(E e) {
        obj = e;
    }

    } // END class Node

    [...]

    public E[] toArray(E[] a) {

    Node n = root;

    a = sort(n, a);
    return a;

    }

    public E[] sort(Node n, E[] a) { //, int idx, E[] a) {

    if (n.left != null) {
        current = n.left;
        sort(current, a);
    }


    a[i] = current.obj;
    i++;

    if (n.right != null) {
        current = n.right;
        sort(current, a);
        }

    return a;

    } // END public Node sort

    [...]

} // END class BinarySearchTree

测试输出:

java.lang.AssertionError: toArray() 不返回集合中的所有元素。:TestPerson("Bender").compareTo(TestPerson("Fry")) == 0 预期:true 但为:false 在 inf1010.assignment.IfiCollectionTest.assertCompareToEquals(IfiCollectionTest.java:74) 在 inf1010.assignment.IfiCollectionTest.assertCompareToEquals(IfiCollectionTest.java:83) 在 inf1010.assignment.IfiCollectionTest.assertCompareToEqualsNoOrder(IfiCollectionTest.java:100) 在 inf1010.assignment.IfiCollectionTest.toArray(IfiCollectionTest.java:202)

protected void assertCompareToEquals(TestPerson actual,
        TestPerson expected, String msg) {
            assertTrue(actual.compareTo(expected) == 0, String.format( // l:74
            "%s: %s.compareTo(%s) == 0", msg, actual, expected));
}

    [...]

protected void assertCompareToEquals(TestPerson[] actual,
        TestPerson[] expected, String msg) {
    for (int i = 0; i < actual.length; i++) {
        TestPerson a = actual[i];
        TestPerson e = expected[i];
        assertCompareToEquals(a, e, msg); // l:83
    }
}

    [...]

protected void assertCompareToEqualsNoOrder(TestPerson[] actual,
        TestPerson[] expected, String msg) {
    assertEquals(actual.length, expected.length, msg);

    TestPerson[] actualElements = new TestPerson[actual.length];
    System.arraycopy(actual, 0, actualElements, 0, actual.length);

    TestPerson[] expectedElements = new TestPerson[expected.length];
    System.arraycopy(expected, 0, expectedElements, 0, expected.length);

    Arrays.sort(expectedElements);
    Arrays.sort(actualElements);

    assertCompareToEquals(actualElements, expectedElements, msg); // l:100
}

    [...]

@Test(dependsOnGroups = { "collection-core" },
    description="Tests if method toArray yields all the elements inserted in the collection in sorted order with smallest item first.")
public void toArray() {
    TestPerson[] actualElements = c.toArray(new TestPerson[c.size()]);

    for (int i = 0; i < actualElements.length; i++) {
        assertNotNull(actualElements[i],
                "toArray() - array element at index " + i + " is null");
    }

    TestPerson[] expectedElements = allElementsAsArray();
    assertCompareToEqualsNoOrder(actualElements, expectedElements, // l:202
            "toArray() does not return all the elements in the collection.");

    Arrays.sort(expectedElements);
    assertCompareToEquals(actualElements, expectedElements,
            "toArray() does not return the elements in sorted order with "
                    + "the smallest elements first.");


    TestPerson[] inArr = new TestPerson[NAMES.length + 1];
    inArr[NAMES.length] = new TestPerson("TEMP");
    actualElements = c.toArray(inArr);
    assertNull(actualElements[NAMES.length],
            "The the element in the array immediately following the "
            + "end of the list is not set to null");
}

不知道该不该多发些测试代码,篇幅挺多的,一篇文章可能有点过头了?

【问题讨论】:

  • 您的来源不完整。 currenti 声明在哪里?
  • 请发布您测试的输入和您得到的输出。

标签: java binary-search-tree


【解决方案1】:

好的,我认为问题在于您使用了“全局”变量current。它的设置方式,没有多大意义。无论如何您都不需要,因为“当前”Node 是参数中提供的那个。

您还应该考虑重命名您的函数。你在这里没有排序任何东西,只是收集树的内容,所以像collect 这样的名字会更合适。

public E[] toArray(E[] a) {
  Node n = root;
  a = collect(n, a);
  return a;
}

public E[] collect(Node n, E[] a) {

  if (n.left != null) {
    // If there is a left (smaller) value, we go there first
    collect(n.left, a);
  }


  // Once we've got all left (smaller) values we can
  // collect the value of out current Node.
  a[i] = n.obj;
  i++;

  if (n.right != null) {
    // And if there is a right (larger) value we get it next
    collect(n.right, a);
  }

  return a;
}

(免责声明:我没有测试过这个)


没有全局索引的替代实现:

public E[] toArray(E[] a) {
  Node n = root;
  collect(n, a, 0);
  return a;
}

public int collect(Node n, E[] a, int i) {

  if (n.left != null) {
    // If there is a left (smaller) value, we go there first
    i = collect(n.left, a, i);
  }


  // Once we've got all left (smaller) values we can
  // collect the value of out current Node.
  a[i] = n.obj;
  i++;

  if (n.right != null) {
    // And if there is a right (larger) value we get it next
    i = collect(n.right, a, i);
  }

  return i;
}

【讨论】:

  • 是的,当然,当前节点显然是多余的 :) 我似乎离解决这个问题又近了一步。现在我得到一个 ArrayIndexOutOfBoundsException。我可以与之合作;)
  • @Askel 当您不使用它时,我看不出在 collect 方法中返回 a 有什么意义。简单的例子... root 有一个左节点... 你调用 collect(n, a) -> collect(n.left, a) -> a[0] = n.left.obj -> i++ -> return a首先在堆栈上收集调用 -> a[1] = n.obj -> return a... 不会使连接方法无效,因为您的 toArray 将返回数组,更有意义吗?
  • @kriegar:好点子。我正在考虑并让它返回索引,这样你就可以摆脱“全局”i
  • @Askel:可能是您多次调用 toArray 吗?在这种情况下,越界异常是因为 i 没有被重置,这是使用“全局”变量的危险之一。我添加了一个替代实现,可以解决这个问题。
  • 是的!就是这样。非常感谢!
【解决方案2】:

我看到你有代码

if (n.left != null) {
        current = n.left;
        sort(current, a);
  }

但我似乎无法找到您在哪个点将电流设置回当前节点,以便当您这样做时

a[i] = current.obj;

你得到正确的结果。这可能就是你没有得到所有结果的原因。无论如何,我看不出(至少从您发布的代码片段中)为什么 current 需要是一个类变量,而不仅仅是在 sort 方法中声明。一般来说,如果您并不真正需要类变量,则不应使用它们。

编辑: 您可以像这样在左子节点上调用排序后将当前节点设置回您正在处理的节点

current = n;
a[i] = current.obj;
i++;

或者根本不使用 current 在这种情况下你会有类似的东西

if (n.left != null)
    sort(n.left, a);
a[i] = n.obj;
i++;
if (n.right != null)
    sort(n.right, a);

【讨论】:

  • 能否请您详细说明在当前节点设置电流?
【解决方案3】:

我认为您感到困惑的地方在于,如果您检查二叉搜索树的工作原理,它是否始终是排序的。您从根节点开始,然后当您插入一个新节点时,它会根据值将其插入到适当的位置(即左侧或右侧)。因此,您不必首先调用 sort 。所以我会从那里开始,然后阅读二叉搜索树。例如wikipedia 有一篇不错的文章。

更新:忽略我的评论,您也不需要这样做。假设您按顺序将 8、3、7、9、12、2、10、1 插入到树中。它最终应该是这样的:

      8
     / \
    3   9
   / \   \
  2   7   12
 /       /
1       10

如果你看它意味着按顺序获取它们,从根开始,然后如果它左边有一个节点到左边,如果没有,返回它自己,如果它有一个到右边价值。对遇到的每个节点重复此操作。

【讨论】:

  • 我明白这一点,并且我确信我已正确添加了元素。至少 add() 方法通过了测试。但是,仍然需要某种算法来正确地将包含元素的节点添加到数组中(从最小到最大)?
  • @Aksel 您的建议更像是对数字进行两次排序。为什么不在二叉搜索树中编写一个方法,返回树中的最小值(每次最左边)并将其从树中删除。您可以随时使用这些值构建数组,直到它为空。
  • 也许这是个好主意,但我从老师那里得到了递归方法的提示,我只是不明白。在您提到的wikipedia article 中,它描述了如何按顺序检索树的元素,这是我想要实现的。
【解决方案4】:

http://cs.armstrong.edu/liang/intro8e/html/BinaryTree.html

最简单的方法是按顺序遍历树并附加到 ArrayList。要获取数组,您可以调用 arrayList 的 .toArray() 方法。

如果你不能使用arraylist,在inordertraversal和increment之外声明一个索引和一个数组,你需要知道树中有多少元素来声明你的数组。

伪代码:

variables:
arraysize = root.count()
E[] inOrderNodeArray = new E[arraysize]
int index = 0

inorder traversal:
void inorder(Node n) {
    if (n) {
        inorder(n.left)
        inOrderNodeArray[index] = n
        index++
        inorder(n.right)
    }
}

【讨论】:

    猜你喜欢
    • 2016-10-27
    • 2020-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多