Existe-t-il une bonne structure de données disponible (Java standard) pour représenter un arbre en Java?
Plus précisément, je dois représenter les éléments suivants:
Existe-t-il une structure disponible pour cela ou dois-je créer la mienne (dans ce cas, des suggestions de mise en œuvre seraient formidables).
Ici:
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;
}
}
C'est une arborescence de base qui peut être utilisée pour String
ou tout autre objet. Il est assez facile de mettre en œuvre des arbres simples pour faire ce dont vous avez besoin.
Tout ce que vous devez ajouter sont des méthodes d’ajout, de suppression, de traversée et de constructeurs. La Node
est la pierre angulaire de la Tree
.
Encore une autre arborescence:
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 ...
}
Exemple d'utilisation:
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
Voir arbre à part entière avec:
Il existe en fait une très bonne structure arborescente dans le JDK.
Regardez javax.swing.tree , TreeModel , et TreeNode . Ils sont conçus pour être utilisés avec la JTreePanel
mais ils constituent en fait une très bonne implémentation d’arborescence et rien ne vous empêche de l’utiliser sans interface pivotante.
Notez qu'à partir de Java 9, vous souhaiterez peut-être ne pas utiliser ces classes car elles ne figureront pas dans le 'Profils compacts' .
Et ça?
import Java.util.ArrayList;
import Java.util.Collection;
import Java.util.HashMap;
/**
* @author [email protected] (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;
}
}
I écrit une petite bibliothèque qui gère les arbres génériques. C'est beaucoup plus léger que le swing. J'ai aussi un projet maven pour cela.
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;
}
}
Évidemment, vous pouvez ajouter des méthodes utilitaires pour ajouter/supprimer des enfants.
Vous devriez commencer par définir ce qu'est un arbre (pour le domaine). Pour ce faire, définissez d'abord interface. Toutes les arborescences ne sont pas modifiables, étant capable d'ajouter et de supprimer des nœuds devrait être une fonctionnalité optionnelle, donc nous faisons une interface supplémentaire pour cela.
Il n'est pas nécessaire de créer des objets nœud contenant les valeurs, je considère en fait cela comme un défaut de conception majeur et une surcharge dans la plupart des implémentations d'arborescence. Si vous regardez Swing, la TreeModel
ne contient pas de classes de nœuds (seul DefaultTreeModel
utilise TreeNode
), car elles ne sont pas vraiment nécessaires.
public interface Tree <N extends Serializable> extends Serializable {
List<N> getRoots ();
N getParent (N node);
List<N> getChildren (N node);
}
Structure d'arborescence modifiable (permet d'ajouter et de supprimer des nœuds):
public interface MutableTree <N extends Serializable> extends Tree<N> {
boolean add (N parent, N node);
boolean remove (N node, boolean cascade);
}
Étant donné ces interfaces, le code qui utilise des arbres n'a pas à se préoccuper beaucoup de la façon dont l'arbre est implémenté. Cela vous permet d'utiliser des implémentations génériques ainsi que spécialisées , où vous réalisez l’arbre en déléguant des fonctions à une autre API.
Exemple: arborescence de fichiers
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();
}
}
Exemple: arborescence générique (basé sur les relations parent/enfant):
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);
}
}
}
Aucune réponse ne mentionne un code trop simpliste mais fonctionnel, donc voici:
public class TreeNodeArray<T> {
public T value;
public final Java.util.List<TreeNodeArray<T>> kids = new Java.util.ArrayList<TreeNodeArray<T>>();
}
Vous pouvez utiliser n'importe quelle API XML de Java en tant que Document et Node..as, XML est une structure arborescente avec des chaînes.
Si vous codez un tableau blanc, passez une entrevue ou envisagez tout simplement d’utiliser un arbre, leur verbosité est un peu exagérée.
Il faut en plus dire que la raison pour laquelle un arbre ne se trouve pas là, comme par exemple une Pair
(à propos de laquelle on pourrait dire la même chose), est parce que vous devriez encapsuler vos données dans la classe qui les utilise, - and la mise en oeuvre la plus simple ressemble à ceci:
/***
/* 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;
}
C'est vraiment ça pour un arbre de largeur arbitraire.
Si vous voulez un arbre binaire, il est souvent plus facile de l'utiliser avec des champs nommés:
private class Node { // Using package visibility is an option
String value;
Node left;
Node right;
}
Ou si vous vouliez un essai:
private class Node {
String value;
Map<char, Node> nodes;
}
Maintenant tu as dit que tu voulais
pour pouvoir obtenir tous les enfants (une sorte de liste ou de tableau de chaînes) à partir d'une chaîne d'entrée représentant un nœud donné
Cela ressemble à vos devoirs.
Mais puisque je suis à peu près sûr que toute échéance est maintenant passée…
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()));
}
}
Cela vous amène à utiliser comme:
$ 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
[]
Il existe quelques structures de données arborescentes en Java, telles que DefaultMutableTreeNode dans JDK Swing, le package Tree dans un analyseur syntaxique Stanford et d'autres codes de jouets. Mais aucun d’entre eux n’est suffisant et pourtant suffisamment petit pour un usage général.
Java-tree le projet tente de fournir une autre structure de données d'arborescence à usage général en Java. La différence entre ceci et les autres sont
Dans le même ordre d'idées que la réponse de Gareth, vérifiez DefaultMutableTreeNode . Ce n'est pas générique, mais autrement semble correspondre à la facture. Même si cela se trouve dans le package javax.swing, cela ne dépend pas des classes AWT ou Swing. En fait, le code source a en fait le commentaire // ISSUE: this class depends on nothing in AWT -- move to Java.util?
public abstract class Node {
List<Node> children;
public List<Node> getChidren() {
if (children == null) {
children = new ArrayList<>();
}
return chidren;
}
}
Aussi simple que possible et très facile à utiliser. Pour l'utiliser, étendez-le:
public class MenuItem extends Node {
String label;
String href;
...
}
Puisque la question demande une structure de données disponible, un arbre peut être construit à partir de listes ou de tableaux:
Object[] tree = new Object[2];
tree[0] = "Hello";
{
Object[] subtree = new Object[2];
subtree[0] = "Goodbye";
subtree[1] = "";
tree[1] = subtree;
}
instanceof
peut être utilisé pour déterminer si un élément est un sous-arbre ou un nœud de terminal.
J'ai écrit une petite classe "TreeMap" basée sur "HashMap" qui prend en charge l'ajout de chemins:
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);
}
}
Il peut être utilisé pour stocker un arbre d'objets de type "T" (générique), mais ne prend pas (encore) en charge le stockage de données supplémentaires dans ses nœuds. Si vous avez un fichier comme celui-ci:
root, child 1
root, child 1, child 1a
root, child 1, child 1b
root, child 2
root, child 3, child 3a
Ensuite, vous pouvez en faire un arbre en exécutant:
TreeMap<String> root = new TreeMap<>();
Scanner scanner = new Scanner(new File("input.txt"));
while (scanner.hasNextLine()) {
root.put(scanner.nextLine().split(", "));
}
Et vous obtiendrez un bel arbre. Il devrait être facile de s'adapter à vos besoins.
Dans le passé, je viens d'utiliser une carte imbriquée pour cela. C’est ce que j’utilise aujourd’hui, c’est très simple mais cela répond à mes besoins. Peut-être que cela aidera un autre.
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"));
}
}
Par exemple :
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;
}
}
// 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);
}
}
Vous pouvez utiliser la classe HashTree incluse dans Apache JMeter qui fait partie du projet Jakarta.
La classe HashTree est incluse dans le package org.Apache.jorphan.collections. Bien que ce package ne soit pas publié en dehors du projet JMeter, vous pouvez l'obtenir facilement:
1) Téléchargez le sources JMeter .
2) Créez un nouveau package.
3) Copiez dessus/src/jorphan/org/Apache/jorphan/collections /. Tous les fichiers sauf Data.Java
4) Copiez également /src/jorphan/org/Apache/jorphan/util/JOrphanUtils.Java
5) HashTree est prêt à être utilisé.
Il n'y a pas de structure de données spécifique dans Java qui convienne à vos besoins. Vos exigences sont assez spécifiques et pour cela vous devez concevoir votre propre structure de données. En examinant vos besoins, n'importe qui peut affirmer que vous avez besoin d'une sorte d'arbre n-aire doté de fonctionnalités spécifiques. Vous pouvez concevoir votre structure de données de la manière suivante:
Je suggérerais que vous écriviez la structure du nœud dans une classe telle que Class Node _ {String value; Répertoriez les enfants;} et toutes les autres méthodes telles que search, insert et getChildren dans une autre classe NodeUtils, de sorte que vous puissiez également transmettre la racine de l'arborescence pour effectuer des opérations sur une arborescence spécifique, comme par exemple: class NodeUtils {public static Node search ( Node root, String value) {// exécute BFS et renvoie Node}
J'ai écrit une bibliothèque d'arbres qui fonctionne bien avec Java8 et qui n'a pas d'autres dépendances. Il fournit également une interprétation approximative de certaines idées issues de la programmation fonctionnelle et vous permet de mapper/filtrer/élaguer/rechercher l’arbre ou les sous-arbres entiers.
https://github.com/RutledgePaulV/Prune
L'implémentation ne fait rien de spécial avec l'indexation et je ne me suis pas éloigné de la récursion. Il est donc possible qu'avec des arbres de grande taille, les performances se dégradent et que vous puissiez détruire la pile. Mais si tout ce dont vous avez besoin est un arbre simple de profondeur faible à modérée, je pense que cela fonctionne assez bien. Il fournit une définition d'égalité saine (basée sur la valeur) et une implémentation toString qui vous permet de visualiser l'arbre!
Veuillez vérifier le code ci-dessous, où j'ai utilisé des structures de données Tree, sans utiliser les classes Collection. Le code peut contenir des bugs/améliorations, mais veuillez l’utiliser juste pour référence.
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();
}
}
Vous pouvez utiliser la classe TreeSet dans Java.util. *. Il fonctionne comme un arbre de recherche binaire, il est donc déjà trié. La classe TreeSet implémente les interfaces Iterable, Collection et Set. Vous pouvez traverser l'arbre avec l'itérateur comme un ensemble.
TreeSet<String> treeSet = new TreeSet<String>();
Iterator<String> it = treeSet.Iterator();
while(it.hasNext()){
...
}