Come implementare una struttura dati ad albero in Java? [chiuso]


496

Esiste una classe di libreria Java standard per rappresentare un albero in Java?

In particolare, devo rappresentare quanto segue:

  • Il sottoalbero in qualsiasi nodo può avere un numero arbitrario di figli
  • Ogni nodo (dopo la radice) e i relativi figli avranno valore stringa
  • Ho bisogno di ottenere tutti i bambini (una sorta di elenco o matrice di stringhe) di un dato nodo e il suo valore di stringa (cioè un metodo che prenderà un nodo come input e restituirà tutti i valori di stringa del nodo children come output)

C'è qualche struttura disponibile per questo o devo crearne una mia (in tal caso i suggerimenti di implementazione sarebbero grandiosi).


3
Se stai utilizzando Java 8 e desideri attraversare i tuoi nodi con flussi, filtri, ecc .; allora potresti dare un'occhiata a Durian github.com/diffplug/durian
Ned Twigg

Risposte:


306

Qui:

public class Tree<T> {
    private Node<T> root;

    public Tree(T rootData) {
        root = new Node<T>();
        root.data = rootData;
        root.children = new ArrayList<Node<T>>();
    }

    public static class Node<T> {
        private T data;
        private Node<T> parent;
        private List<Node<T>> children;
    }
}

Questa è una struttura ad albero di base che può essere utilizzata per Stringqualsiasi altro oggetto. È abbastanza facile implementare alberi semplici per fare ciò di cui hai bisogno.

Tutto quello che devi aggiungere sono i metodi per aggiungere, rimuovere, attraversare e costruire. Il Nodeè il blocco base di Tree.


304
A rigor di termini la Treeclasse non è necessaria, perché ogni cosa Nodepuò essere vista come un albero.
Joachim Sauer,

34
@Joa, mi piace avere una struttura per contenere la radice. È possibile aggiungere metodi alla classe dell'albero che hanno più senso chiamare su un albero anziché su un singolo nodo.
jjnguy,

24
@Justin: per esempio? Questa è una domanda onesta: non riesco a pensare a un singolo metodo che abbia un senso su tutto l'albero che non ha senso su un sottoalbero.
Joachim Sauer,

22
Sono d'accordo con @Joa che la classe Tree non è necessaria. Preferisco lasciare esplicita la natura ricorsiva degli alberi nel codice non aggiungendo una classe Tree separata e usando costantemente la classe Node. Invece, desidero nominare una variabile "albero" o "radice" se deve essere chiaro che si ha a che fare con il nodo radice di un albero.
jvdbogae,

89
@Joa Un bambino di quattro anni sono completamente d'accordo con te. Nix la classe degli alberi.
jjnguy,

122

Ancora un'altra struttura ad albero:

public class TreeNode<T> implements Iterable<TreeNode<T>> {

    T data;
    TreeNode<T> parent;
    List<TreeNode<T>> children;

    public TreeNode(T data) {
        this.data = data;
        this.children = new LinkedList<TreeNode<T>>();
    }

    public TreeNode<T> addChild(T child) {
        TreeNode<T> childNode = new TreeNode<T>(child);
        childNode.parent = this;
        this.children.add(childNode);
        return childNode;
    }

    // other features ...

}

Esempio di utilizzo:

TreeNode<String> root = new TreeNode<String>("root");
{
    TreeNode<String> node0 = root.addChild("node0");
    TreeNode<String> node1 = root.addChild("node1");
    TreeNode<String> node2 = root.addChild("node2");
    {
        TreeNode<String> node20 = node2.addChild(null);
        TreeNode<String> node21 = node2.addChild("node21");
        {
            TreeNode<String> node210 = node20.addChild("node210");
        }
    }
}

BONUS
Vedi l'albero a tutti gli effetti con:

  • iteratore
  • ricerca
  • Java / C #

https://github.com/gt4dev/yet-another-tree-structure


2
Ho appena trovato la tua libreria estremamente utile. Grazie. Ma vorrei sapere come implementare dinamicamente la struttura ad albero in base alla relazione di riferimento tra genitore e figlio. L'esempio dato è che ho un membro 1 sponsor un altro membro2 e membro 2 sponsor membro 3 e così via. Ho già la relazione tra i record della tabella, ma non sono sicuro di poterli inserire in un albero usando la tua libreria.
d4v1dv00

1
a partire dal 2016, il collegamento non contiene file di origine o download
Sharon Ben Asher,

2
A mio avviso, questa risposta, tre anni dopo quella sopra classificata, è quella più pulita. Tuttavia, vorrei sostituire la LinkedList con una ArrayList per this.children.
HopeKing

1
Vorrei usare un set per i bambini.
DPM,

Potrei sbagliarmi, ma sembra che con questa implementazione, è necessario chiamare hasNext()prima di ogni chiamata next()per ottenere risultati validi. Questo non fa parte delle Iteratorspecifiche.
Robert Lewis

97

In realtà esiste una struttura ad albero piuttosto buona implementata nel JDK.

Dai un'occhiata a javax.swing.tree , TreeModel e TreeNode . Sono progettati per essere utilizzati con il JTreePanelma sono, in effetti, un'implementazione ad albero piuttosto buona e non c'è nulla che ti impedisca di usarlo senza un'interfaccia swing.

Si noti che a partire da Java 9 è possibile che non si desideri utilizzare queste classi in quanto non saranno presenti nei "Profili compatti" .


5
Sì, li ho usati in passato e faranno tutto ciò che vuoi da un albero. L'unico aspetto negativo che mi viene in mente è la lunghezza dei nomi delle rispettive classi di implementazione: DefaultTreeModel e DefaultMutableTreeNode. Verbo, ma non che sia così importante, immagino.
Ultimate Gobblement

4
Un buon modo per affrontarlo è creare un paio di metodi statici newModel () e newNode () e quindi importarli staticamente.
Gareth Davis,

140
Eviterei di utilizzare le librerie Swing su funzioni non correlate a Swing. Questa è una cattiva pratica di programmazione . Non si sa mai come Swing implementa i loro alberi, quali sono le loro dipendenze e come ciò potrebbe cambiare in futuro. Swing non è una libreria di utilità ma una libreria UI.
Jasas,

44
Penso che una cattiva pratica di programmazione sia un po 'dura.
Gareth Davis,

51
javax.swing.tree.TreeModel è un'interfaccia pubblica (esattamente come java.util.List) e non avrà modifiche incompatibili. Un ulteriore vantaggio è che puoi facilmente eseguire il debug / visualizzare il tuo albero durante lo sviluppo.
lbalazscs,

45

Che dire di questo?

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

/**
  * @author ycoppel@google.com (Yohann Coppel)
  * 
  * @param <T>
  *          Object's type in the tree.
*/
public class Tree<T> {

  private T head;

  private ArrayList<Tree<T>> leafs = new ArrayList<Tree<T>>();

  private Tree<T> parent = null;

  private HashMap<T, Tree<T>> locate = new HashMap<T, Tree<T>>();

  public Tree(T head) {
    this.head = head;
    locate.put(head, this);
  }

  public void addLeaf(T root, T leaf) {
    if (locate.containsKey(root)) {
      locate.get(root).addLeaf(leaf);
    } else {
      addLeaf(root).addLeaf(leaf);
    }
  }

  public Tree<T> addLeaf(T leaf) {
    Tree<T> t = new Tree<T>(leaf);
    leafs.add(t);
    t.parent = this;
    t.locate = this.locate;
    locate.put(leaf, t);
    return t;
  }

  public Tree<T> setAsParent(T parentRoot) {
    Tree<T> t = new Tree<T>(parentRoot);
    t.leafs.add(this);
    this.parent = t;
    t.locate = this.locate;
    t.locate.put(head, this);
    t.locate.put(parentRoot, t);
    return t;
  }

  public T getHead() {
    return head;
  }

  public Tree<T> getTree(T element) {
    return locate.get(element);
  }

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

  public Collection<T> getSuccessors(T root) {
    Collection<T> successors = new ArrayList<T>();
    Tree<T> tree = getTree(root);
    if (null != tree) {
      for (Tree<T> leaf : tree.leafs) {
        successors.add(leaf.head);
      }
    }
    return successors;
  }

  public Collection<Tree<T>> getSubTrees() {
    return leafs;
  }

  public static <T> Collection<T> getSuccessors(T of, Collection<Tree<T>> in) {
    for (Tree<T> tree : in) {
      if (tree.locate.containsKey(of)) {
        return tree.getSuccessors(of);
      }
    }
    return new ArrayList<T>();
  }

  @Override
  public String toString() {
    return printTree(0);
  }

  private static final int indent = 2;

  private String printTree(int increment) {
    String s = "";
    String inc = "";
    for (int i = 0; i < increment; ++i) {
      inc = inc + " ";
    }
    s = inc + head;
    for (Tree<T> child : leafs) {
      s += "\n" + child.printTree(increment + indent);
    }
    return s;
  }
}

5
Come verrebbe implementato DFS su un albero creato usando questo oggetto di classe?
leba-lev,

3
Come verrebbe implementata la rimozione di una foglia usando questa classe?
ghedas,

2
A cosa servirebbe il campo principale?
Acour83,

2
Sarebbe bello se questa classe avesse della documentazione. Non capisco bene quali metodi piacciano setAsParento getHeadfacciano e questo è il momento in cui potrei davvero ottenere un aiuto sulle strutture dei dati degli alberi. Anche la fonte originale del documento non ha commenti.
disasterkid,

23

Ho scritto una piccola biblioteca che gestisce alberi generici. È molto più leggero della roba swing. Ho anche un progetto maven per questo.


3
Lo sto usando ora, funziona alla grande. Ho dovuto cambiare la fonte in modo significativo per le mie personalizzazioni, ma è stato un ottimo punto di partenza. Grazie Vivin!
Jasas,

20
public class Tree {
    private List<Tree> leaves = new LinkedList<Tree>();
    private Tree parent = null;
    private String data;

    public Tree(String data, Tree parent) {
        this.data = data;
        this.parent = parent;
    }
}

Ovviamente puoi aggiungere metodi di utilità per aggiungere / rimuovere figli.


1
Ciò suggerisce che il genitore di un albero è un albero. Credo che tu stia provando a creare una classe Tree Node.
Madhur Bhargava,

16

Dovresti iniziare definendo cosa sia un albero (per il dominio), questo è meglio definendo prima l' interfaccia . Non tutte le strutture ad albero sono modificabili, essere in grado di aggiungere e rimuovere nodi dovrebbe essere una funzione opzionale, quindi creiamo un'interfaccia aggiuntiva per questo.

Non è necessario creare oggetti nodo che contengano i valori , in effetti lo vedo come un grave difetto di progettazione e sovraccarico nella maggior parte delle implementazioni degli alberi. Se guardi Swing, TreeModelè privo di classi di nodi (ne DefaultTreeModelfa solo uso TreeNode), in quanto non sono realmente necessarie.

public interface Tree <N extends Serializable> extends Serializable {
    List<N> getRoots ();
    N getParent (N node);
    List<N> getChildren (N node);
}

Struttura ad albero mutevole (consente di aggiungere e rimuovere nodi):

public interface MutableTree <N extends Serializable> extends Tree<N> {
    boolean add (N parent, N node);
    boolean remove (N node, boolean cascade);
}

Date queste interfacce, il codice che utilizza gli alberi non deve preoccuparsi molto di come viene implementato l'albero. Ciò consente di utilizzare implementazioni generiche e specializzate , in cui realizzare l'albero delegando le funzioni a un'altra API.

Esempio: struttura ad albero dei file

public class FileTree implements Tree<File> {

    @Override
    public List<File> getRoots() {
        return Arrays.stream(File.listRoots()).collect(Collectors.toList());
    }

    @Override
    public File getParent(File node) {
        return node.getParentFile();
    }

    @Override
    public List<File> getChildren(File node) {
        if (node.isDirectory()) {
            File[] children = node.listFiles();
            if (children != null) {
                return Arrays.stream(children).collect(Collectors.toList());
            }
        }
        return Collections.emptyList();
    }
}

Esempio: struttura ad albero generica (basata su relazioni padre / figlio):

public class MappedTreeStructure<N extends Serializable> implements MutableTree<N> {

    public static void main(String[] args) {

        MutableTree<String> tree = new MappedTreeStructure<>();
        tree.add("A", "B");
        tree.add("A", "C");
        tree.add("C", "D");
        tree.add("E", "A");
        System.out.println(tree);
    }

    private final Map<N, N> nodeParent = new HashMap<>();
    private final LinkedHashSet<N> nodeList = new LinkedHashSet<>();

    private void checkNotNull(N node, String parameterName) {
        if (node == null)
            throw new IllegalArgumentException(parameterName + " must not be null");
    }

    @Override
    public boolean add(N parent, N node) {
        checkNotNull(parent, "parent");
        checkNotNull(node, "node");

        // check for cycles
        N current = parent;
        do {
            if (node.equals(current)) {
                throw new IllegalArgumentException(" node must not be the same or an ancestor of the parent");
            }
        } while ((current = getParent(current)) != null);

        boolean added = nodeList.add(node);
        nodeList.add(parent);
        nodeParent.put(node, parent);
        return added;
    }

    @Override
    public boolean remove(N node, boolean cascade) {
        checkNotNull(node, "node");

        if (!nodeList.contains(node)) {
            return false;
        }
        if (cascade) {
            for (N child : getChildren(node)) {
                remove(child, true);
            }
        } else {
            for (N child : getChildren(node)) {
                nodeParent.remove(child);
            }
        }
        nodeList.remove(node);
        return true;
    }

    @Override
    public List<N> getRoots() {
        return getChildren(null);
    }

    @Override
    public N getParent(N node) {
        checkNotNull(node, "node");
        return nodeParent.get(node);
    }

    @Override
    public List<N> getChildren(N node) {
        List<N> children = new LinkedList<>();
        for (N n : nodeList) {
            N parent = nodeParent.get(n);
            if (node == null && parent == null) {
                children.add(n);
            } else if (node != null && parent != null && parent.equals(node)) {
                children.add(n);
            }
        }
        return children;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        dumpNodeStructure(builder, null, "- ");
        return builder.toString();
    }

    private void dumpNodeStructure(StringBuilder builder, N node, String prefix) {
        if (node != null) {
            builder.append(prefix);
            builder.append(node.toString());
            builder.append('\n');
            prefix = "  " + prefix;
        }
        for (N child : getChildren(node)) {
            dumpNodeStructure(builder, child, prefix);
        }
    }
}

1
Sto affrontando un problema quando seguo questa struttura quando faccio tree.add ("A", "B"); tree.add ("A", "C"); tree.add ("C", "D"); tree.add ("E", "A"); E è un genitore di A Come possiamo fare questo?
saNiks,

1
Ciao saNicks, nel codice sopra c'era un bug che non faceva aggiungere l'ultima relazione. Ora è stato risolto e ho anche aggiunto controlli non nulli e (più importante): controlli ciclici che impediscono di violare la struttura ad albero (aggiungendo un codice o uno dei suoi antenati come figlio a se stesso). Grazie per il suggerimento!
Peter Walser,

1
Ho risolto il bug se qualcuno sta cercando una correzione per quel bug quello che devi fare è vedere se il metodo add restituisce false e se è falso basta creare un nuovo temp di LinkedHashSet <N> e clonare il nodelist dell'albero in esso quindi puoi cancellare l'albero, aggiungi il nodo padre che non è stato aggiunto nel passaggio precedente e poi aggiungiTutto il tempNode di nuovo all'albero padre ... Grazie per la struttura!
saNiks,

2
Rimuovi quegli inutili modificatori pubblici dalle tue interfacce.
Hamedz,

1
come posso generare un array json da questo
Pawan Pandey

12

Nessuna risposta menziona un codice semplificato ma funzionante, quindi eccolo qui:

public class TreeNodeArray<T> {
    public T value;
    public final  java.util.List<TreeNodeArray<T>> kids =  new java.util.ArrayList<TreeNodeArray<T>>();
}

10

È possibile utilizzare qualsiasi API XML di Java come documento e nodo..come XML è una struttura ad albero con stringhe


5
Idea eccellente, potremmo usare nella memoria lo schema XML usando dom4j + jaxen xpath per cercare i nodi.
Ben Rhouma Zied

8

Se stai programmando la lavagna, un colloquio o anche solo pianificando di usare un albero, la verbosità di questi è un po 'troppo.

Va inoltre detto che il motivo per cui un albero non è lì dentro, ad esempio un Pair(circa il quale si potrebbe dire lo stesso), è perché dovresti incapsulare i tuoi dati nella classe utilizzandolo e l'implementazione più semplice assomiglia a:

/***
/* Within the class that's using a binary tree for any reason. You could 
/* generalize with generics IFF the parent class needs different value types.
 */
private class Node {
  public String value;
  public Node[] nodes; // Or an Iterable<Node> nodes;
}

Questo è tutto per un albero di larghezza arbitraria.

Se volevi un albero binario è spesso più facile da usare con i campi con nome:

private class Node { // Using package visibility is an option
  String value;
  Node left;
  Node right;
}

O se volevi un trie:

private class Node {
  String value;
  Map<char, Node> nodes;
}

Ora hai detto che vuoi

per essere in grado di ottenere a tutti i bambini (una sorta di elenco o matrice di stringhe) una stringa di input che rappresenta un determinato nodo

Sembra i tuoi compiti.
Ma dato che sono ragionevolmente sicuro che sia scaduta una scadenza ...

import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;

public class kidsOfMatchTheseDays {
 static private class Node {
   String value;
   Node[] nodes;
 }

 // Pre-order; you didn't specify.
 static public List<String> list(Node node, String find) {
   return list(node, find, new ArrayList<String>(), false);
 }

 static private ArrayList<String> list(
     Node node,
     String find,
     ArrayList<String> list,
     boolean add) {
   if (node == null) {
     return list;
   }
   if (node.value.equals(find)) {
     add = true;
   }
   if (add) {
     list.add(node.value);
   }
   if (node.nodes != null) {
     for (Node child: node.nodes) {
       list(child, find, list, add);
     }
   }
   return list;
 }

 public static final void main(String... args) {
   // Usually never have to do setup like this, so excuse the style
   // And it could be cleaner by adding a constructor like:
   //     Node(String val, Node... children) {
   //         value = val;
   //         nodes = children;
   //     }
   Node tree = new Node();
   tree.value = "root";
   Node[] n = {new Node(), new Node()};
   tree.nodes = n;
   tree.nodes[0].value = "leftish";
   tree.nodes[1].value = "rightish-leafy";
   Node[] nn = {new Node()};
   tree.nodes[0].nodes = nn;
   tree.nodes[0].nodes[0].value = "off-leftish-leaf";
   // Enough setup
   System.out.println(Arrays.toString(list(tree, args[0]).toArray()));
 }
}

Questo ti fa usare come:

$ java kidsOfMatchTheseDays leftish
[leftish, off-leftish-leaf]
$ java kidsOfMatchTheseDays root
[root, leftish, off-leftish-leaf, rightish-leafy]
$ java kidsOfMatchTheseDays rightish-leafy
[rightish-leafy]
$ java kidsOfMatchTheseDays a
[]

7

Sulla stessa linea della risposta di Gareth, controlla DefaultMutableTreeNode . Non è generico, ma per il resto sembra adattarsi al conto. Anche se è nel pacchetto javax.swing, non dipende da alcuna classe AWT o Swing. In effetti, il codice sorgente ha effettivamente il commento// ISSUE: this class depends on nothing in AWT -- move to java.util?


Lol, come ti sei mai imbattuto in questa classe?
Pacerier,

7

Esistono un paio di strutture di dati ad albero in Java, come DefaultMutableTreeNode in JDK Swing, Albero nel pacchetto parser Stanford e altri codici giocattolo. Ma nessuno di questi è sufficiente ma abbastanza piccolo per scopi generali.

Il progetto Java-tree tenta di fornire un'altra struttura di dati ad albero per scopi generici in Java. La differenza tra questo e gli altri sono

  • Totalmente gratuito. Puoi usarlo ovunque (tranne che per i compiti: P)
  • Piccolo ma abbastanza generale. Ho messo tutto della struttura dei dati in un file di classe, quindi sarebbe facile da copiare / incollare.
  • Non solo un giocattolo. Sono a conoscenza di dozzine di codici albero Java che possono gestire solo alberi binari o operazioni limitate. Questo TreeNode è molto più di questo. Fornisce diversi modi di visitare i nodi, come preordine, postordine, breadthfstst, foglie, percorso verso radice, ecc. Inoltre, gli iteratori sono forniti anche per la sufficienza.
  • Verranno aggiunti altri programmi di utilità. Sono disposto ad aggiungere più operazioni per rendere completo questo progetto, specialmente se invii una richiesta tramite github.


5

Poiché la domanda richiede una struttura di dati disponibile, è possibile creare un albero da elenchi o matrici:

Object[] tree = new Object[2];
tree[0] = "Hello";
{
  Object[] subtree = new Object[2];
  subtree[0] = "Goodbye";
  subtree[1] = "";
  tree[1] = subtree;
}

instanceof può essere usato per determinare se un elemento è una sottostruttura o un nodo terminale.


2
Abbastanza brutto. E non funziona, se i tuoi oggetti dati possono essere matrici o rispettivamente elenchi.
user686249

1
Sono d'accordo che è brutto. Gli Objects sarebbero gli oggetti foglia (ad esempio, Strings) o i rami (rappresentati da matrici). E funziona: quel codice verrà compilato e crea un piccolo albero di Strings.
Olathe,

5
public abstract class Node {
  List<Node> children;

  public List<Node> getChidren() {
    if (children == null) {
      children = new ArrayList<>();
    }
    return chidren;
  }
}

Semplice come diventa e molto facile da usare. Per usarlo, estenderlo:

public class MenuItem extends Node {
  String label;
  String href;
  ...
}

3

Per esempio :

import java.util.ArrayList;
import java.util.List;



/**
 * 
 * @author X2
 *
 * @param <T>
 */
public class HisTree<T> 
{
    private Node<T> root;

    public HisTree(T rootData) 
    {
        root = new Node<T>();
        root.setData(rootData);
        root.setChildren(new ArrayList<Node<T>>());
    }

}

class Node<T> 
{

    private T data;
    private Node<T> parent;
    private List<Node<T>> children;

    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
    public Node<T> getParent() {
        return parent;
    }
    public void setParent(Node<T> parent) {
        this.parent = parent;
    }
    public List<Node<T>> getChildren() {
        return children;
    }
    public void setChildren(List<Node<T>> children) {
        this.children = children;
    }
}

3

In passato ho appena usato una mappa nidificata per questo. Questo è quello che uso oggi, è molto semplice ma si adatta alle mie esigenze. Forse questo aiuterà un altro.

import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * Created by kic on 16.07.15.
 */
public class NestedMap<K, V> {
    private final Map root = new HashMap<>();

    public NestedMap<K, V> put(K key) {
        Object nested = root.get(key);

        if (nested == null || !(nested instanceof NestedMap)) root.put(key, nested = new NestedMap<>());
        return (NestedMap<K, V>) nested;
    }

    public Map.Entry<K,V > put(K key, V value) {
        root.put(key, value);

        return (Map.Entry<K, V>) root.entrySet().stream().filter(e -> ((Map.Entry) e).getKey().equals(key)).findFirst().get();
    }

    public NestedMap<K, V> get(K key) {
        return (NestedMap<K, V>) root.get(key);
    }

    public V getValue(K key) {
        return (V) root.get(key);
    }

    @JsonValue
    public Map getRoot() {
        return root;
    }

    public static void main(String[] args) throws Exception {
        NestedMap<String, Integer> test = new NestedMap<>();
        test.put("a").put("b").put("c", 12);
        Map.Entry<String, Integer> foo = test.put("a").put("b").put("d", 12);
        test.put("b", 14);
        ObjectMapper mapper = new ObjectMapper();
        System.out.println(mapper.writeValueAsString(test));

        foo.setValue(99);
        System.out.println(mapper.writeValueAsString(test));

        System.out.println(test.get("a").get("b").getValue("d"));
    }
}

3

Ho scritto una piccola classe "TreeMap" basata su "HashMap" che supporta l'aggiunta di percorsi:

import java.util.HashMap;
import java.util.LinkedList;

public class TreeMap<T> extends LinkedHashMap<T, TreeMap<T>> {

    public void put(T[] path) {
        LinkedList<T> list = new LinkedList<>();
        for (T key : path) {
            list.add(key);
        }
        return put(list);
    }

    public void put(LinkedList<T> path) {
        if (path.isEmpty()) {
            return;
        }
        T key = path.removeFirst();
        TreeMap<T> val = get(key);
        if (val == null) {
            val = new TreeMap<>();
            put(key, val);
        }
        val.put(path);
    }

}

Può essere utilizzato per archiviare un albero di cose di tipo "T" (generico), ma non (ancora) supporta l'archiviazione di dati extra nei suoi nodi. Se hai un file come questo:

root, child 1
root, child 1, child 1a
root, child 1, child 1b
root, child 2
root, child 3, child 3a

Quindi puoi renderlo un albero eseguendo:

TreeMap<String> root = new TreeMap<>();
Scanner scanner = new Scanner(new File("input.txt"));
while (scanner.hasNextLine()) {
  root.put(scanner.nextLine().split(", "));
}

E otterrai un bell'albero. Dovrebbe essere facile adattarsi alle tue esigenze.


2

È possibile utilizzare la classe HashTree inclusa in Apache JMeter che fa parte del Progetto Jakarta.

La classe HashTree è inclusa nel pacchetto org.apache.jorphan.collections. Sebbene questo pacchetto non sia rilasciato al di fuori del progetto JMeter, puoi ottenerlo facilmente:

1) Scarica le fonti JMeter .

2) Crea un nuovo pacchetto.

3) Copia su di esso / src / jorphan / org / apache / jorphan / collections /. Tutti i file tranne Data.java

4) Copia anche /src/jorphan/org/apache/jorphan/util/JOrphanUtils.java

5) HashTree è pronto per l'uso.


2

Non esiste una struttura di dati specifica in Java adatta alle tue esigenze. Le vostre esigenze sono piuttosto specifiche e per questo è necessario progettare la propria struttura di dati. Osservando le tue esigenze, chiunque può dire che hai bisogno di una sorta di albero n-ary con alcune funzionalità specifiche. È possibile progettare la struttura dei dati nel modo seguente:

  1. La struttura del nodo dell'albero sarebbe simile al contenuto del nodo e all'elenco dei figli come: class Node {String value; Elenco figli;}
  2. Devi recuperare i figli di una determinata stringa, quindi puoi avere 2 metodi 1: Nodo searchNode (String str), restituirà il nodo che ha lo stesso valore dell'input dato (usa BFS per la ricerca) 2: Elenco getChildren (String str): questo metodo chiamerà internamente il searchNode per ottenere il nodo con la stessa stringa e quindi creerà l'elenco di tutti i valori di stringa dei figli e ritornerà.
  3. Ti verrà inoltre richiesto di inserire una stringa nell'albero. Dovrai scrivere un metodo per dire void insert (String parent, String value): questo cercherà di nuovo il nodo con valore uguale a parent e quindi puoi creare un Nodo con un valore dato e aggiungere all'elenco dei figli al genitore trovato .

Suggerirei di scrivere la struttura del nodo in una classe come Class Node {String value; Elenca i figli;} e tutti gli altri metodi come ricerca, inserimento e getChildren in un'altra classe NodeUtils in modo da poter anche passare la radice dell'albero per eseguire operazioni su un albero specifico come: class NodeUtils {ricerca del nodo statico pubblico (radice del nodo, valore String) {// esegue BFS e restituisce nodo}


2
    // TestTree.java
// A simple test to see how we can build a tree and populate it
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;

public class TestTree extends JFrame {

  JTree tree;
  DefaultTreeModel treeModel;

  public TestTree( ) {
    super("Tree Test Example");
    setSize(400, 300);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
  }

  public void init( ) {
    // Build up a bunch of TreeNodes. We use DefaultMutableTreeNode because the
    // DefaultTreeModel can use it to build a complete tree.
    DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
    DefaultMutableTreeNode subroot = new DefaultMutableTreeNode("SubRoot");
    DefaultMutableTreeNode leaf1 = new DefaultMutableTreeNode("Leaf 1");
    DefaultMutableTreeNode leaf2 = new DefaultMutableTreeNode("Leaf 2");

    // Build our tree model starting at the root node, and then make a JTree out
    // of it.
    treeModel = new DefaultTreeModel(root);
    tree = new JTree(treeModel);

    // Build the tree up from the nodes we created.
    treeModel.insertNodeInto(subroot, root, 0);
    // Or, more succinctly:
    subroot.add(leaf1);
    root.add(leaf2);

    // Display it.
    getContentPane( ).add(tree, BorderLayout.CENTER);
  }

  public static void main(String args[]) {
    TestTree tt = new TestTree( );
    tt.init( );
    tt.setVisible(true);
  }
}

3
Ti preghiamo di non solo scaricare il codice: spiega cosa fa e soprattutto perché differisce (è meglio) rispetto a tutte le altre risposte.
Jan Doggen,

2

Ho scritto una libreria ad albero che funziona bene con Java8 e che non ha altre dipendenze. Fornisce inoltre un'interpretazione libera di alcune idee dalla programmazione funzionale e consente di mappare / filtrare / potare / cercare l'intero albero o sottotitoli.

https://github.com/RutledgePaulV/prune

L'implementazione non fa nulla di speciale con l'indicizzazione e non mi sono allontanato dalla ricorsione, quindi è possibile che con alberi di grandi dimensioni le prestazioni peggiorino e potresti far saltare lo stack. Ma se tutto ciò di cui hai bisogno è un albero semplice di profondità da piccola a moderata, penso che funzioni abbastanza bene. Fornisce una definizione sana (basata sul valore) di uguaglianza e ha anche un'implementazione toString che ti consente di visualizzare l'albero!


1

Si prega di controllare il codice seguente, dove ho usato le strutture dati Tree, senza usare le classi Collection. Il codice potrebbe presentare bug / miglioramenti, ma si prega di utilizzare questo solo come riferimento

package com.datastructure.tree;

public class BinaryTreeWithoutRecursion <T> {

    private TreeNode<T> root;


    public BinaryTreeWithoutRecursion (){
        root = null;
    }


    public void insert(T data){
        root =insert(root, data);

    }

    public TreeNode<T>  insert(TreeNode<T> node, T data ){

        TreeNode<T> newNode = new TreeNode<>();
        newNode.data = data;
        newNode.right = newNode.left = null;

        if(node==null){
            node = newNode;
            return node;
        }
        Queue<TreeNode<T>> queue = new Queue<TreeNode<T>>();
        queue.enque(node);
        while(!queue.isEmpty()){

            TreeNode<T> temp= queue.deque();
            if(temp.left!=null){
                queue.enque(temp.left);
            }else
            {
                temp.left = newNode;

                queue =null;
                return node;
            }
            if(temp.right!=null){
                queue.enque(temp.right);
            }else
            {
                temp.right = newNode;
                queue =null;
                return node;
            }
        }
        queue=null;
        return node; 


    }

    public void inOrderPrint(TreeNode<T> root){
        if(root!=null){

            inOrderPrint(root.left);
            System.out.println(root.data);
            inOrderPrint(root.right);
        }

    }

    public void postOrderPrint(TreeNode<T> root){
        if(root!=null){

            postOrderPrint(root.left);

            postOrderPrint(root.right);
            System.out.println(root.data);
        }

    }

    public void preOrderPrint(){
        preOrderPrint(root);
    }


    public void inOrderPrint(){
        inOrderPrint(root);
    }

    public void postOrderPrint(){
        inOrderPrint(root);
    }


    public void preOrderPrint(TreeNode<T> root){
        if(root!=null){
            System.out.println(root.data);
            preOrderPrint(root.left);
            preOrderPrint(root.right);
        }

    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        BinaryTreeWithoutRecursion <Integer> ls=  new BinaryTreeWithoutRecursion <>();
        ls.insert(1);
        ls.insert(2);
        ls.insert(3);
        ls.insert(4);
        ls.insert(5);
        ls.insert(6);
        ls.insert(7);
        //ls.preOrderPrint();
        ls.inOrderPrint();
        //ls.postOrderPrint();

    }

}

2
" senza usare le classi Collection " Ah? Quindi da dove viene la classe Queue? E come detto sopra, è un albero binario, fallendo al primo requisito (qualsiasi numero di nodi figlio).
PhiLho,

1

È possibile utilizzare la classe TreeSet in java.util. *. Funziona come l'albero di ricerca binario, quindi è già ordinato. La classe TreeSet implementa le interfacce Iterable, Collection e Set. Puoi attraversare l'albero con iteratore come un set.

TreeSet<String> treeSet = new TreeSet<String>();
Iterator<String> it  = treeSet.Iterator();
while(it.hasNext()){
...
}

Puoi controllare, Java Doc e alcuni altri .


-1

Strumento Custom Tree di Tree senza usare il framework Collection. Contiene diverse operazioni fondamentali necessarie per l'implementazione dell'albero.

class Node {

    int data;
    Node left;
    Node right;

    public Node(int ddata, Node left, Node right) {
        this.data = ddata;
        this.left = null;
        this.right = null;      
    }

    public void displayNode(Node n) {
        System.out.print(n.data + " "); 
    }

}

class BinaryTree {

    Node root;

    public BinaryTree() {
        this.root = null;
    }

    public void insertLeft(int parent, int leftvalue ) {
        Node n = find(root, parent);
        Node leftchild = new Node(leftvalue, null, null);
        n.left = leftchild;
    }

    public void insertRight(int parent, int rightvalue) {
        Node n = find(root, parent);
        Node rightchild = new Node(rightvalue, null, null);
        n.right = rightchild;
    }

    public void insertRoot(int data) {
        root = new Node(data, null, null);
    }

    public Node getRoot() {
        return root;
    }

    public Node find(Node n, int key) {     
        Node result = null;

        if (n == null)
            return null;

        if (n.data == key)
            return n;

        if (n.left != null)
            result = find(n.left, key);

        if (result == null)
            result = find(n.right, key);

        return result;
    } 

    public int getheight(Node root){
        if (root == null)
            return 0;

        return Math.max(getheight(root.left), getheight(root.right)) + 1; 
    }

    public void printTree(Node n) {     
        if (n == null)
            return;

        printTree(n.left);
        n.displayNode(n);
        printTree(n.right);             
    }

}

11
È un albero binario, non riesce al primo requisito dell'OP ...
PhiLho,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.