【问题标题】:Sort an unordered tree by the amount of child-nodes a node has按节点拥有的子节点数量对无序树进行排序
【发布时间】:2017-09-11 21:29:24
【问题描述】:

我目前正在自己​​研究 Robert Sedgewick 的“Java 算法”(第 3 版,德语版),并试图解决其中的一个练习,目前是“创意解决方案”部分的一个练习。

作为一个练习的解决方案的一部分,我尝试按每个节点拥有的子节点数量对未排序的树进行排序,具有大量子节点的节点首先出现:

  1. 根据子节点本身拥有的子节点nNodes 的数量对父n0 的所有子节点进行排序,从左到右从大到小排序。对树中的所有节点执行此排序。
  2. 如果这些子节点中的 2 个兄弟节点 n1 和 n2 具有相同的nNodes,则转到 n1 和 n2 的子节点尝试根据它们最大子节点的 nNodes 对这两个节点进行排序。如果没有发现差异,尝试按照他们的第二大孩子的nNodes等排序。
  3. 如果你的孩子用完了,去 2 中孩子数量最多的孩子的孩子节点,重复 2. etc. etc.
  4. 如果经过所有比较后发现 n1 和 n2 都是形状相同的树的根,那么两者中的哪一个先出现并不重要。

为了进行直观比较,此“规则”将导致如下所示的树排序:Example of Sorting

代码

我遇到的问题是正确实现排序。每棵树都由节点组成。每个节点都包含一个值(这样您就有一个节点的“名称”,对于排序本身并不重要,它只关心每个节点具有的子节点数量)和一个节点引用的数组列表,该节点引用的子节点节点。对于没有子节点的节点,ArrayList 的大小为 0。这里的关键是对所有节点中的 ArrayLists 进行排序,我目前正在尝试使用带有 Comparator 对象的内置排序方法进行排序。我认为我必须递归地处理这个问题,这使得整个事情变得非常混乱,因为我目前有一个比较器方法调用自身,同时还在同一方法中为其他 ArraLists 调用“排序”。下面的sortForest 方法在我用一些主要方法进行测试时给了我堆栈溢出错误。

    static NodeComparator comp = new NodeComparator();

    static class Node {
        int value;
        ArrayList<Node> children;

        Node(int value, ArrayList<Node> children) {
            this.value = value;
            this.children = children;
        }
    }

    static class NodeComparator implements Comparator<Node> {
        public NodeComparator() {
        }

        public int compare(Node n1, Node n2) {
            /*-
             * Base Case 1: Both Nodes are leafs (isEmpty() is true) - they are
             * equal -> return 0.
             * Base Case 2/3: One of the Nodes is a leaf while the other isn't - 
             * the one that is a leaf is "lower" than the one that isn't. ->
             * return (-) 1. 
             */
            if (n1.children.isEmpty() && n2.children.isEmpty()) {
                return 0;
            } else if (n2.children.isEmpty()) {
                n1.children.sort(comp);
                return 1;
            } else if (n1.children.isEmpty()) {
                n2.children.sort(comp);
                return -1;
            } else {
                /* Get the amount of children the 2 nodes n1 and n2 have */
                int nChildren1 = (n1.children.isEmpty()) ? 0 : n1.children.size();
                int nChildren2 = (n2.children.isEmpty()) ? 0 : n2.children.size();
                /* Always sort the ArrayList of children that they have */
                n1.children.sort(comp);
                n2.children.sort(comp);

                /*
                 * If n1 and n2 have equal amounts of children, compare the
                 * amounts of children their children have, from largest to
                 * lowest
                 */
                if (nChildren1 == nChildren2) {
                    int result = 0;
                    for (int i = 0; (i < nChildren1) && (result == 0); i++) {
                        compare(n1.children.get(i), n2.children.get(i));
                    }
                    return result;
                } else {
                    /*- If one node has more children than the other, sort accordingly */
                    return ((nChildren1 > nChildren2) ? 1 : -1);
                }
            }
        }
    }

    static void sortForest(Node root) {
        for (int i = 0; i < root.children.size(); i++) {
            sortForest(root.children.get(i));
            root.children.sort(comp);
        }
        return;
    }

问题

如何让这段代码工作?我有点确定这大致处于正确解决方案的范围内,但是我已经尝试了几个小时来思考这个问题,但无法弄清楚。我确信这给了我堆栈溢出,因为那里有一个永无止境的递归,我只是看不到它。一般来说,递归给我带来了在心理上正确执行此操作的问题。我找不到与此相同的问题,而那些相似的问题则关注二叉树而不是无序树。

【问题讨论】:

    标签: java sorting tree stack-overflow


    【解决方案1】:

    根据树的大小,您在调用sortForest 时可能会遇到有关 Java 的默认堆栈大小的限制。解决方法包括通过-Xss JVM 选项增加它,在Thread constructor 中设置堆栈大小,以非递归方式重写它或使用Trampoline pattern

    【讨论】:

    • 我正在运行的测试树目前有 14 个节点,所以我目前确信我在那里的某个地方有一个永无止境的递归。应该在主要问题中提到这一点,我将对其进行编辑。
    【解决方案2】:

    上面的代码有几个问题:

    1. 代码假设每棵树的根节点不包含对自身的引用(上面代码中没有显示),导致compare方法一次次调用根节点,已修正。

    2. 在比较方法中调用排序是绝对没有必要的,实际上是错误的。代码已经在为每个带有sortForest() 的节点调用排序方法,从叶子开始,所以那里没有位置,需要从比较方法的所有代码部分中删除。

    3. 对于给定的返回,compare 方法不会导致从最大到最小的排序,而是从最小到最大的排序。它需要在返回 -1 的地方返回 1,反之亦然。

    4. compare 方法还需要在其 for 循环中将 compare() 的返回值存储在 result 中,否则结果永远不会改变,并且一旦发现不相似,循环也不会停止。

    5. sortForest() 中使用root.children.sort(comp); 的排序绝对必须在forLoop 之外进行,否则您对一些ArrayList 进行排序,而您仍然需要它们按原始顺序执行所有操作正确调用。

    在纠正所有这些之后,上面的 sortForest()compare() 方法提供了正确的排序结果,例如树:

    int[] tree1 = { 2, 3, 3, 3, 2, 2, 1, 0, 7, 5, 3, 10, 10, 6 };

    int[] tree2 = { 4, 10, 11, 0, 4, 0, 12, 4, 7, 8, 0, 3, 4, 12 };

    并按照this picture所示对它们进行排序。

    解决方案的完整代码以及一些优化和删除不必要的代码以及固定排序可以找到here

    【讨论】:

      猜你喜欢
      • 2019-07-14
      • 1970-01-01
      • 2020-08-14
      • 1970-01-01
      • 1970-01-01
      • 2019-05-21
      • 1970-01-01
      • 2011-08-09
      • 2017-09-02
      相关资源
      最近更新 更多