【问题标题】:Algorithm for Modified Preorder Tree Traversal in JavaJava中改进的前序树遍历算法
【发布时间】:2022-10-19 17:56:34
【问题描述】:

本着How to repair a corrupted MPTT tree (nested set) in the database using SQL? 的精神,我试图找出一种算法来确定给定根节点的Java 中修改的预序树遍历的左右值。有没有人有任何经验将常规的预购遍历转换为修改后的遍历?

我目前将此作为我的预购遍历。

List<Node> preorderTraversal(Node root) {
    List<Node> list = new ArrayList<>();
    if(root == null) return list;
    Stack<Node> stack = new Stack<>();
    stack.push(root);

    while(!stack.empty()) {
        root = stack.pop();
        list.add(root);

        if(root.children != null) {
            for(Node child : root.children) {
                if(child != null) {
                    stack.push(child);
                }
            }
        }
    }
    return list;
}

【问题讨论】:

  • 你能举一个你想要的遍历输出的例子吗?

标签: java tree-traversal


【解决方案1】:

首先,您的前序遍历代码以相反的顺序遍历子代。当您按顺序将孩子推入堆栈时,它们会以相反的顺序弹出,导致不正确的行为。它应该是这样的:

static List<Node> preorderTraversal(Node root) {
    List<Node> list = new ArrayList<>();
    if (root == null) return list;
    Stack<Node> stack = new Stack<>();
    stack.push(root);

    while (!stack.empty()) {
        root = stack.pop();
        list.add(root);

        if (root.children != null) {
            // iterate in reverse
            for (int i = root.children.size() - 1; i >= 0; i--) {
                Node child = root.children.get(i);
                if (child != null) {
                    stack.push(child);
                }
            }
        }
    }
    return list;
}

在我们查看修改后的前序遍历之前,看看如何递归地实现前序遍历会很有帮助:

static List<Node> preorderTraversalRecursive(Node root) {
    ArrayList<Node> outList = new ArrayList<>();
    preorderTraversalRecursive(root, outList);
    return outList;
}

private static void preorderTraversalRecursive(Node root, ArrayList<Node> outList) {
    if (root == null) {
        return;
    }
    outList.add(root);

    if (root.children != null) {
        for (Node child : root.children) {
            preorderTraversalRecursive(child, outList);
        }
    }
}

此代码在遍历其子节点之前简单地输出一个节点。

要将其变为修改后的前序遍历,您只需要跟踪在每个节点处理之前和之后递增的计数器,并在处理子节点之前和之后记录它以获得leftright价值观。在这里,当前的count 由方法返回,以便在处理子节点期间可以更改它,并且此更新值用于其父节点的right 值:

static List<MPTTNode> modifiedPreorderTraversalRecursive(Node root) {
    ArrayList<MPTTNode> outList = new ArrayList<>();
    modifiedPreorderTraversalRecursive(root, 0, outList);
    return outList;
}

private static int modifiedPreorderTraversalRecursive(Node root, int counter, ArrayList<MPTTNode> outList) {
    if (root == null) {
        return counter;
    }
    counter++;
    MPTTNode mpttNode = new MPTTNode(root.data, counter, 0); // right value is unknown, leave as 0 for now
    outList.add(mpttNode);

    if (root.children != null) {
        for (Node child : root.children) {
            // modify counter
            counter = modifiedPreorderTraversalRecursive(child, counter, outList);
        }
    }
    counter++;

    mpttNode.right = counter;

    return counter;
}

这也可以迭代地实现:

static List<MPTTNode> modifiedPreorderTraversal(Node root) {
    List<MPTTNode> list = new ArrayList<>();
    if (root == null) return list;
    Stack<Node> stack = new Stack<>();
    Stack<Integer> pending = new Stack<>();
    stack.push(root);

    int counter = 0;
    while (!stack.empty()) {
        root = stack.pop();
        if (root == null) {
            int nodeIndex = pending.pop();
            counter++;
            list.get(nodeIndex).right = counter;
            continue;
        }
        counter++;
        pending.push(list.size());
        list.add(new MPTTNode(root.data, counter, 0)); // right value is unknown, leave as 0 for now


        stack.push(null);
        if (root.children != null) {
            // iterate in reverse
            for (int i = root.children.size() - 1; i >= 0; i--) {
                Node child = root.children.get(i);
                if (child != null) {
                    stack.push(child);
                }
            }
        }
    }
    return list;
}

这通过使用pending 堆栈来跟踪输出列表(list)中父节点的索引来工作。它使用stack 堆栈中的null 值来表示节点的所有子节点都已被处理,因此其父节点的right 值是已知的。


这是我的所有代码,包括与链接问题中使用的相同的示例树:

import java.util.Arrays;
import java.util.Collections;
import java.util.List;


public class Main {
    public static void main(String[] args) {
        Node tree = new Node("Electronics",
                Arrays.asList(
                        new Node("Televisions",
                                Arrays.asList(
                                        new Node("Tube"),
                                        new Node("LCD"),
                                        new Node("Plasma")
                                )
                        ),
                        new Node("Portable Electronics",
                                Arrays.asList(
                                        new Node("MP3 Players", Collections.singletonList(
                                                new Node("Flash")
                                        )),
                                        new Node("CD Players"),
                                        new Node("2 Way Radios")
                                )
                        )
                )
        );

        List<MPTTNode> list1 = Node.modifiedPreorderTraversal(tree);
        List<MPTTNode> list2 = Node.modifiedPreorderTraversalRecursive(tree);

        if (!list1.equals(list2)) {
            throw new RuntimeException("Traversals not equal");
        }

        for (var node : list1) {
            System.out.printf("%-30s left:%5d, right:%5d
", node.data, node.left, node.right);
        }
    }
}
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class Node {
    String data;
    List<Node> children;

    public Node(String data, List<Node> children) {
        this.data = data;
        this.children = children;
    }

    public Node(String data) {
        this.data = data;
    }

    static List<Node> preorderTraversal(Node root) {
        List<Node> list = new ArrayList<>();
        if (root == null) return list;
        Stack<Node> stack = new Stack<>();
        stack.push(root);

        while (!stack.empty()) {
            root = stack.pop();
            list.add(root);

            if (root.children != null) {
                // iterate in reverse
                for (int i = root.children.size() - 1; i >= 0; i--) {
                    Node child = root.children.get(i);
                    if (child != null) {
                        stack.push(child);
                    }
                }
            }
        }
        return list;
    }

    static List<MPTTNode> modifiedPreorderTraversal(Node root) {
        List<MPTTNode> list = new ArrayList<>();
        if (root == null) return list;
        Stack<Node> stack = new Stack<>();
        Stack<Integer> pending = new Stack<>();
        stack.push(root);

        int counter = 0;
        while (!stack.empty()) {
            root = stack.pop();
            if (root == null) {
                int nodeIndex = pending.pop();
                counter++;
                list.get(nodeIndex).right = counter;
                continue;
            }
            counter++;
            pending.push(list.size());
            list.add(new MPTTNode(root.data, counter, 0)); // right value is unknown, leave as 0 for now


            stack.push(null);
            if (root.children != null) {
                // iterate in reverse
                for (int i = root.children.size() - 1; i >= 0; i--) {
                    Node child = root.children.get(i);
                    if (child != null) {
                        stack.push(child);
                    }
                }
            }
        }
        return list;
    }

    static List<Node> preorderTraversalRecursive(Node root) {
        ArrayList<Node> outList = new ArrayList<>();
        preorderTraversalRecursive(root, outList);
        return outList;
    }

    private static void preorderTraversalRecursive(Node root, ArrayList<Node> outList) {
        if (root == null) {
            return;
        }
        outList.add(root);

        if (root.children != null) {
            for (Node child : root.children) {
                preorderTraversalRecursive(child, outList);
            }
        }
    }
    static List<MPTTNode> modifiedPreorderTraversalRecursive(Node root) {
        ArrayList<MPTTNode> outList = new ArrayList<>();
        modifiedPreorderTraversalRecursive(root, 0, outList);
        return outList;
    }

    private static int modifiedPreorderTraversalRecursive(Node root, int counter, ArrayList<MPTTNode> outList) {
        if (root == null) {
            return counter;
        }
        counter++;
        MPTTNode mpttNode = new MPTTNode(root.data, counter, 0);
        outList.add(mpttNode);

        if (root.children != null) {
            for (Node child : root.children) {
                counter = modifiedPreorderTraversalRecursive(child, counter, outList);
            }
        }
        counter++;

        mpttNode.right = counter;

        return counter;
    }
}
import java.util.Objects;

public class MPTTNode {
    String data;
    int left;
    int right;

    public MPTTNode(String data, int left, int right) {
        this.data = data;
        this.left = left;
        this.right = right;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MPTTNode mpttNode = (MPTTNode) o;
        return left == mpttNode.left && right == mpttNode.right && Objects.equals(data, mpttNode.data);
    }
}

输出:

Electronics                    left:    1, right:   20
Televisions                    left:    2, right:    9
Tube                           left:    3, right:    4
LCD                            left:    5, right:    6
Plasma                         left:    7, right:    8
Portable Electronics           left:   10, right:   19
MP3 Players                    left:   11, right:   14
Flash                          left:   12, right:   13
CD Players                     left:   15, right:   16
2 Way Radios                   left:   17, right:   18

【讨论】:

    猜你喜欢
    • 2010-11-06
    • 2015-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-17
    相关资源
    最近更新 更多