9 Stimmen

Java-Baumstruktur mit mehreren Kindern (sortiert) auf jeder Ebene

Ich arbeite mit einer flachen Liste von Objekten, die jedoch in Eltern-Kind-Beziehungen miteinander verbunden sind. Ein Objekt kann eine beliebige Anzahl von Kindern haben, oder auch gar keine. Ich muss diese Objekte als Baum anzeigen, der diese Beziehungen zeigt. Jede Ebene des Baums soll sortiert werden (die Objekte sind kompatibel mit Collections.sort() ).

Die Frage ist zweigeteilt:

  1. Hat Java eine gute Out-of-the-Box-Datenstruktur für die Speicherung eines solchen Baums, oder muss ich eine von Grund auf neu schreiben? (keine große Aufgabe, aber es macht keinen Sinn, das Rad neu zu erfinden) Ich weiß über DefaultTreeModel in Swing... aber diese Anwendung läuft auf der Serverseite, und die Verwendung des Swing-Pakets wird bei der Codeüberprüfung missbilligt werden.

  2. Was wäre das beste Muster für das Laden einer flachen Liste in eine solche Datenstruktur? Mein erster Gedanke ist, die Objekte auf der Root-Ebene zu identifizieren und dann eine rekursive Methode zu verwenden, um durch ihre Kinder, Enkelkinder usw. zu gehen. Für die Anforderung der Sortierung der Peers auf jeder Ebene im Baum... Ich bin mir nicht sicher, ob es sinnvoller ist, sich darüber Gedanken zu machen, wenn ich den Baum aufbaue, oder ob ich mir darüber später Gedanken mache, wenn ich den Baum für die Anzeige parse.

9voto

Sean Patrick Floyd Punkte 283617

Hier ist eine schnelle und einfache Tree-Implementierung, die TreeSets auf allen Ebenen verwendet (Sie können einen Komparator angeben, oder es wird die natürliche Reihenfolge verwendet):

public class Tree<T> {

    private final Node<T> rootElement;

    public void visitNodes(final NodeVisitor<T> visitor){
        doVisit(rootElement, visitor);
    }

    private static <T> boolean doVisit(final Node<T> node,
        final NodeVisitor<T> visitor){
        boolean result = visitor.visit(node);
        if(result){
            for(final Node<T> subNode : node.children){
                if(!doVisit(subNode, visitor)){
                    result = false;
                    break;
                }
            }
        }
        return result;
    }

    public interface NodeVisitor<T> {

        boolean visit(Node<T> node);
    }

    public Node<T> getRootElement(){
        return rootElement;
    }

    private static final class NodeComparator<T> implements Comparator<Node<T>>{

        private final Comparator<T> wrapped;

        @Override
        public int compare(final Node<T> o1, final Node<T> o2){
            return wrapped.compare(o1.value, o2.value);
        }

        public NodeComparator(final Comparator<T> wrappedComparator){
            this.wrapped = wrappedComparator;
        }

    }

    public static class Node<T> {

        private final SortedSet<Node<T>> children;

        private final Node<T> parent;

        private T value;

        private final Comparator<?> comparator;

        @SuppressWarnings("unchecked")
        Node(final T value, final Node<T> parent, final Comparator<?> comparator){
            this.value = value;
            this.parent = parent;
            this.comparator = comparator;
            children =
                new TreeSet<Node<T>>(new NodeComparator<T>((Comparator<T>) comparator));
        }

        public List<Node<T>> getChildren(){
            return new ArrayList<Node<T>>(children);
        }

        public Node<T> getParent(){
            return parent;
        }

        public T getValue(){
            return value;
        }

        public void setValue(final T value){
            this.value = value;
        }

        public Node<T> addChild(final T value){
            final Node<T> node = new Node<T>(value, this, comparator);
            return children.add(node) ? node : null;
        }

    }

    @SuppressWarnings("rawtypes")
    private static final Comparator NATURAL_ORDER = new Comparator(){

        @SuppressWarnings("unchecked")
        @Override
        public int compare(final Object o1, final Object o2){
            return ((Comparable) o1).compareTo(o2);
        }
    };

    private final Comparator<?> comparator;

    public Tree(){
        this(null, null);
    }

    public Tree(final Comparator<? super T> comparator){
        this(comparator, null);
    }

    public Tree(final Comparator<? super T> comparator, final T rootValue){
        this.comparator = comparator == null ? NATURAL_ORDER : comparator;
        this.rootElement = new Node<T>(rootValue, null, this.comparator);
    }

    public Tree(final T rootValue){
        this(null, rootValue);
    }

}

Hier ist ein Beispielcode dafür:

final Tree<Integer> tree = new Tree<Integer>();
final Node<Integer> rootNode = tree.getRootElement();
rootNode.setValue(1);
final Node<Integer> childNode = rootNode.addChild(2);
final Node<Integer> newChildNode = rootNode.addChild(3);
newChildNode.addChild(4);
tree.visitNodes(new NodeVisitor<Integer>(){

    @Override
    public boolean visit(final Node<Integer> node){
        final StringBuilder sb = new StringBuilder();
        Node<Integer> curr = node;
        do{
            if(sb.length() > 0){
                sb.insert(0, " > ");
            }
            sb.insert(0, String.valueOf(curr.getValue()));
            curr = curr.getParent();
        } while(curr != null);
        System.out.println(sb);
        return true;
    }
});

出力します。

1
1 > 2
1 > 3
1 > 3 > 4

4voto

Péter Török Punkte 111735

Was wäre das beste Muster für das Laden einer flachen Liste in eine solche Datenstruktur? Mein erster Gedanke ist, die Objekte auf der Root-Ebene zu identifizieren und dann eine rekursive Methode zu verwenden, um durch ihre Kinder, Enkelkinder usw. zu gehen.

Wenn ich es richtig verstehe, haben Sie nur eine flache Liste, ohne konkrete Assoziationen zwischen den Elementen, und Sie können irgendwie feststellen, ob ein bestimmtes Element das Kind eines anderen ist.

In diesem Fall könnten Sie

  1. die Liste sortieren
  2. (Identifizieren Sie den Wurzelknoten, falls er noch nicht bekannt ist)
  3. die Wurzel in eine Warteschlange stellen
  4. den ersten Knoten aus der Warteschlange nehmen
  5. ausgehend vom ersten Element der Liste wird jedes Element daraufhin überprüft, ob es ein Kind des aktuellen Knotens ist; wenn ja, wird es der aktuellen Ebene des Baums hinzugefügt und in die Warteschlange gestellt
  6. ab Schritt 4 wiederholen.

Wenn die Erkennung von Eltern-Kind-Beziehungen kostspielig ist, können Sie die Leistung verbessern, indem Sie ein Kennzeichen für jeden Knoten speichern, dessen Position innerhalb des Baums bereits identifiziert ist, so dass Sie beim Durchlaufen der Liste über diese Knoten springen können. Alternativ können Sie die gesamte sortierte Liste in eine verknüpfte Liste kopieren, so dass es trivial ist, verarbeitete Elemente daraus zu entfernen.

1voto

Navi Punkte 8196

In Java gibt es keine Baumstrukturen, aber es gibt sortierte Strukturen: TreeSet und TreeMap. Siehe für einige Hinweise Java-Datenstruktur zur Simulation eines Datenbaums

0voto

Jason Orendorff Punkte 39655

Ich würde so vorgehen, wie Sie es vorgeschlagen haben.

Wie Sie den Baum aufbauen, hängt davon ab, welche Informationen Sie in der ursprünglichen Liste haben.

  • Wenn jeder Knoten einen Verweis auf seine Eltern und eine Sammlung seiner Kinder enthält, müssen Sie nichts anderes als die Wurzelmenge erstellen.

  • Wenn jeder Knoten nur einen Verweis auf seinen Elternteil hat, müssen Sie einen Baum erstellen, aber Sie können dies in einem einzigen Durchgang über die Daten tun, indem Sie eine HashMap verwenden, um jeden Knoten einer Liste (die Sie erstellen) seiner Kinder zuzuordnen.

  • Wenn die Knoten nicht einmal einen Verweis auf ihre Eltern enthalten, müssen Sie tun, was Péter vorschlägt.

In jedem Fall würde ich mir nicht die Mühe machen, zuerst die gesamte Liste zu sortieren. Das Sortieren einer großen Liste wird langsamer sein als das Sortieren vieler kleiner Listen mit der gleichen Gesamtlänge. (Das folgt daraus, dass Sortieren O(n log n) ist).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X