Javaでツリーを表す標準のJavaライブラリクラスはありますか?
具体的には、以下を表す必要があります。
- 任意のノードのサブツリーは、任意の数の子を持つことができます
- 各ノード(ルートの後)とその子は文字列値を持ちます
- 特定のノードのすべての子(ある種のリストまたは文字列の配列)とその文字列値(つまり、ノードを入力として受け取り、子ノードのすべての文字列値を出力として返すメソッド)を取得する必要があります。
これに利用できる構造はありますか、または自分で作成する必要がありますか?
Javaでツリーを表す標準のJavaライブラリクラスはありますか?
具体的には、以下を表す必要があります。
これに利用できる構造はありますか、または自分で作成する必要がありますか?
回答:
ここに:
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;
}
}
これは、String
または他のオブジェクトに使用できる基本的なツリー構造です。必要なことを行うために単純なツリーを実装するのはかなり簡単です。
追加する必要があるのは、追加、削除、トラバース、およびコンストラクターのメソッドだけです。Node
の基本的なビルディングブロックですTree
。
Tree
クラスNode
自体は必要ありません。クラス自体は、それ自体がツリーと見なすことができるためです。
さらに別のツリー構造:
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 ...
}
使用例:
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");
}
}
}
ボーナス
:本格的なツリーを参照してください:
hasNext()
にnext()
を呼び出す前にを呼び出す必要があるようです。これはIterator
仕様の一部ではありません。
実際、JDKにはかなり良いツリー構造が実装されています。
javax.swing.tree、TreeModel、およびTreeNodeを見てください。彼らはで使用するように設計されていますJTreePanel
が、実際には非常に優れたツリー実装であり、swingインターフェースなしで使用することを妨げるものは何もありません。
Java 9以降では、これらのクラスは「コンパクトプロファイル」に存在しないため、使用しない方がよい場合があります。
これはどうですか?
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;
}
}
setAsParent
かgetHead
ん、これは私が実際にツリーデータ構造上のいくつかの助けを得ることができる時間です。ドキュメントの元のソースでさえ、コメントはありません。
一般的なツリーを処理する小さなライブラリを書きました。スイングのものよりもはるかに軽量です。私はmavenプロジェクトも持っていますます。
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;
}
}
明らかに、子を追加/削除するユーティリティメソッドを追加できます。
(ドメインの)ツリーが何であるかを定義することから始める必要があります。これは、最初にインターフェースを定義することで行うのが最善です。すべてのツリー構造が変更可能であるわけではありません。ノードを追加および削除できることはオプション機能であるため、そのための追加のインターフェースを作成します。
値を保持するノードオブジェクトを作成する必要はありません。実際、これはほとんどのツリー実装における主要な設計上の欠陥とオーバーヘッドであると考えています。Swingを見ると、実際には必要ないためTreeModel
、ノードクラスはありません(のみDefaultTreeModel
を使用しますTreeNode
)。
public interface Tree <N extends Serializable> extends Serializable {
List<N> getRoots ();
N getParent (N node);
List<N> getChildren (N node);
}
変更可能なツリー構造(ノードの追加と削除が可能):
public interface MutableTree <N extends Serializable> extends Tree<N> {
boolean add (N parent, N node);
boolean remove (N node, boolean cascade);
}
これらのインターフェースがあれば、ツリーを使用するコードは、ツリーの実装方法をあまり気にする必要がありません。これにより、汎用的な実装だけでなく、関数を別のAPIに委譲することでツリーを実現する特殊な実装も使用できます。
例:ファイルツリー構造
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();
}
}
例:一般的なツリー構造(親子関係に基づく):
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);
}
}
}
Javaの任意のXML APIをドキュメントおよびノードとして使用できます。XMLは文字列を含むツリー構造です。
ホワイトボードのコーディング、インタビュー、または単にツリーの使用を計画している場合、これらの冗長性はすべて少しです。
さらに、ツリーがそこにない理由は、たとえばa Pair
(同じことが言えるかもしれません)は、それを使用してクラスにデータをカプセル化する必要があり、最も単純な実装が次のようになるためです。
/***
/* 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;
}
実際には、任意の幅のツリーについてはこれで終わりです。
バイナリツリーが必要な場合は、多くの場合、名前付きフィールドを使用する方が簡単です。
private class Node { // Using package visibility is an option
String value;
Node left;
Node right;
}
または、トライが必要な場合:
private class Node {
String value;
Map<char, Node> nodes;
}
今あなたはあなたが欲しいと言いました
特定のノードを表す入力文字列を指定して、すべての子(ある種のリストまたは文字列の配列)を取得できるようにする
宿題のようですね。
しかし、締め切りが過ぎたと私は合理的に確信しているので…
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()));
}
}
これにより、次のように使用できます。
$ 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
[]
Garethの回答と同じように、DefaultMutableTreeNodeを確認してください。それは一般的ではありませんが、それ以外は法案に合うようです。javax.swingパッケージに含まれていますが、AWTクラスやSwingクラスに依存していません。実際、ソースコードには実際にコメントがあります// ISSUE: this class depends on nothing in AWT -- move to java.util?
Javaには、JDK SwingのDefaultMutableTreeNode、StanfordパーサーパッケージのTree、その他のおもちゃのコードなど、いくつかのツリーデータ構造があります。しかし、これらはどれも十分ではありませんが、一般的な目的には十分に小さくありません。
Javaツリープロジェクトは、Javaで別の汎用ツリーデータ構造を提供しようとします。これと他の違いは
質問は利用可能なデータ構造を要求するため、ツリーはリストまたは配列から構築できます。
Object[] tree = new Object[2];
tree[0] = "Hello";
{
Object[] subtree = new Object[2];
subtree[0] = "Goodbye";
subtree[1] = "";
tree[1] = subtree;
}
instanceof
要素がサブツリーかターミナルノードかを判断するために使用できます。
Object
Sはいずれかのリーフオブジェクト(例えば、あろうString
S)または分岐(配列によって表されます)。そして、それは機能します:そのコードはコンパイルされ、の小さなツリーを作成しますString
。
例えば :
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;
}
}
これまでは、ネストされたマップを使用していました。これは私が今日使用しているもので、非常にシンプルですが私のニーズに合います。多分これは別のものを助けるでしょう。
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"));
}
}
パスの追加をサポートする「HashMap」に基づく小さな「TreeMap」クラスを書きました。
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);
}
}
これは、タイプ「T」(汎用)のツリーを格納するために使用できますが、そのノードへの追加データの格納は(まだ)サポートしていません。次のようなファイルがある場合:
root, child 1
root, child 1, child 1a
root, child 1, child 1b
root, child 2
root, child 3, child 3a
次に、次のコマンドを実行してツリーにすることができます。
TreeMap<String> root = new TreeMap<>();
Scanner scanner = new Scanner(new File("input.txt"));
while (scanner.hasNextLine()) {
root.put(scanner.nextLine().split(", "));
}
そして、あなたは素敵な木を手に入れます。ニーズに合わせて簡単に調整できる必要があります。
Jakarta Projectの一部であるApache JMeterに含まれているHashTreeクラスを使用できます。
HashTreeクラスは、パッケージorg.apache.jorphan.collectionsに含まれています。このパッケージはJMeterプロジェクトの外ではリリースされていませんが、簡単に入手できます。
1)JMeterソースをダウンロードする。
2)新しいパッケージを作成します。
3)/ src / jorphan / org / apache / jorphan / collections /にコピーします。Data.javaを除くすべてのファイル
4)/src/jorphan/org/apache/jorphan/util/JOrphanUtils.javaもコピーします
5)HashTreeを使用する準備が整いました。
Javaには、要件に合う特定のデータ構造はありません。要件は非常に具体的であり、そのために独自のデータ構造を設計する必要があります。要件を見ると、特定の機能を備えたある種のn進ツリーが必要だと誰もが言うことができます。次の方法でデータ構造を設計できます。
Class Node {String value;のような1つのクラスでノードの構造を書くことをお勧めします。List children;}、およびsearch、insert、getChildrenなどの他のすべてのメソッドを別のNodeUtilsクラスに追加すると、ツリーのルートを渡して、次のような特定のツリーで操作を実行することもできます:class NodeUtils {public static Node search(Node root、String value) {// BFSを実行してノードを返す}
// 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);
}
}
私はJava8でうまく動作し、他の依存関係がないツリーライブラリを作成しました。また、関数型プログラミングからのアイデアの緩やかな解釈を提供し、ツリー全体またはサブツリーをマップ/フィルター/プルーニング/検索できます。
https://github.com/RutledgePaulV/prune
実装はインデックス作成に関して特別なことは何もせず、再帰から逸脱していなかったため、大きなツリーではパフォーマンスが低下し、スタックを爆破する可能性があります。しかし、必要なのは、深さが小から中程度の単純なツリーであれば、十分に機能すると思います。それは等号の正気な(値ベースの)定義を提供し、ツリーを視覚化できるtoString実装も持っています!
Collectionクラスを使用せずに、Treeデータ構造を使用した以下のコードを確認してください。コードにはバグ/改善があるかもしれませんが、参考のためにこれを使用してください
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();
}
}
java.util。*でTreeSetクラスを使用できます。バイナリ検索ツリーのように機能するため、すでにソートされています。TreeSetクラスは、Iterable、Collection、およびSetインターフェースを実装します。セットのようにイテレータを使用してツリーをトラバースできます。
TreeSet<String> treeSet = new TreeSet<String>();
Iterator<String> it = treeSet.Iterator();
while(it.hasNext()){
...
}
コレクションフレームワークを使用しないツリーのカスタムツリー実装。Treeの実装に必要なさまざまな基本的な操作が含まれています。
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);
}
}