Làm thế nào để thực hiện cấu trúc dữ liệu cây trong Java? [đóng cửa]


496

Có lớp thư viện Java tiêu chuẩn nào để biểu diễn một cây trong Java không?

Cụ thể tôi cần trình bày như sau:

  • Cây con tại bất kỳ nút nào có thể có số lượng con tùy ý
  • Mỗi nút (sau gốc) và con của nó sẽ có giá trị chuỗi
  • Tôi cần lấy tất cả các phần tử con (một số loại danh sách hoặc mảng Chuỗi) của một nút đã cho và đó là giá trị chuỗi (tức là một phương thức sẽ lấy một nút làm đầu vào và trả về tất cả các giá trị chuỗi của nút con làm đầu ra)

Có bất kỳ cấu trúc có sẵn cho điều này hay tôi cần phải tạo riêng của mình (nếu vậy đề xuất thực hiện sẽ là tuyệt vời).


3
Nếu bạn đang sử dụng Java 8 và muốn duyệt qua các nút của mình bằng các luồng, lọc, v.v; sau đó bạn có thể muốn xem Durian github.com/diffplug/durian
Ned Twigg

1
Bạn có thể sử dụng API này: sourceforge.net/p/treeds4j
Mohamed Ennahdi El Idrissi

Câu trả lời:


306

Đây:

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;
    }
}

Đó là một cấu trúc cây cơ bản có thể được sử dụng cho Stringhoặc bất kỳ đối tượng nào khác. Nó khá dễ dàng để thực hiện các cây đơn giản để làm những gì bạn cần.

Tất cả những gì bạn cần thêm là các phương thức để thêm, xóa khỏi, duyệt qua và các hàm tạo. Đây Nodelà khối xây dựng cơ bản của Tree.


304
Nói một cách nghiêm túc, Treelớp học là không cần thiết, bởi vì Nodebản thân mỗi thứ có thể được xem như một cái cây.
Joachim Sauer

34
@Joa, tôi thích có một cấu trúc để chứa root. Bạn có thể thêm các phương thức vào lớp cây có ý nghĩa hơn để gọi trên cây chứ không phải là một nút.
jjnguy

24
@Justin: chẳng hạn? Đó là một câu hỏi trung thực: Tôi không thể nghĩ ra một phương pháp duy nhất có ý nghĩa trên toàn bộ cây không có ý nghĩa trên cây phụ.
Joachim Sauer

22
Tôi đồng ý với @Joa rằng lớp Tree là không cần thiết. Tôi thích để lại bản chất đệ quy của các cây trong mã bằng cách không thêm một lớp Tree riêng biệt và nhất quán sử dụng lớp Node. Thay vào đó, tôi đặt tên cho một biến là 'cây' hoặc 'gốc' nếu cần phải rõ ràng rằng bạn đang xử lý Nút gốc của cây.
jvdbogae

89
@Joa Một đứa trẻ bốn tuổi hoàn toàn đồng ý với bạn. Nix lớp cây.
jjnguy

122

Một cấu trúc cây khác:

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 ...

}

Sử dụng mẫu:

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");
        }
    }
}

THƯỞNG
Xem cây đầy đủ với:

  • vòng lặp
  • đang tìm kiếm
  • Java / C #

https://github.com/gt4dev/yet-another-tree- cấu trúc


2
Chỉ cần tìm thấy thư viện của bạn vô cùng hữu ích. Cảm ơn bạn. Nhưng tôi muốn biết làm thế nào để thực hiện linh hoạt cấu trúc cây dựa trên mối quan hệ tham chiếu giữa cha mẹ và con. Ví dụ đưa ra là tôi có một thành viên1 tài trợ cho một thành viên khác2, và thành viên 2 tài trợ thành viên 3 và cứ thế. Đã có bảng ghi lại mối quan hệ nhưng chỉ cần không chắc chắn tôi có thể đưa chúng vào cây bằng thư viện của bạn.
d4v1dv00

1
kể từ năm 2016, liên kết không chứa tệp nguồn hoặc tải xuống
Sharon Ben Asher

2
Theo quan điểm của tôi, câu trả lời này ba năm sau câu trả lời được đánh giá cao hơn ở trên, là câu trả lời sạch hơn. Tuy nhiên, tôi sẽ thay thế LinkedList bằng ArrayList cho this.children.
HopeKing

1
Tôi sẽ sử dụng một bộ cho trẻ em.
DPM

Tôi có thể sai nhưng có vẻ như với việc thực hiện này, bạn phải gọi hasNext()trước mỗi cuộc gọi next()để có kết quả hợp lệ. Đây không phải là một phần của Iteratorthông số kỹ thuật.
Robert Lewis

97

Thực sự có một cấu trúc cây khá tốt được triển khai trong JDK.

Hãy xem javax.swing.tree , TreeModelTreeNode . Chúng được thiết kế để được sử dụng với JTreePanelnhưng trên thực tế, chúng là một triển khai cây khá tốt và không có gì ngăn bạn sử dụng nó với giao diện xoay.

Lưu ý rằng kể từ Java 9, bạn có thể không muốn sử dụng các lớp này vì chúng sẽ không có trong 'Cấu hình nhỏ gọn' .


5
Vâng tôi đã sử dụng chúng trong quá khứ, và họ sẽ làm mọi thứ bạn muốn từ một cái cây. Nhược điểm duy nhất tôi có thể nghĩ đến là độ dài của tên của các lớp triển khai tương ứng của chúng: DefaultTreeModel và DefaultMutableTreeNode. Verbose, nhưng đó không phải là tất cả những gì quan trọng tôi đoán.
Trận đấu cuối cùng

4
Cách tốt để đối phó với điều đó là tạo ra một vài phương thức tĩnh newModel () và newNode () và sau đó nhập tĩnh chúng.
Gareth Davis

140
Tôi sẽ tránh sử dụng các thư viện Swing trên các chức năng không liên quan đến Swing. Đây là thực hành mã hóa xấu . Bạn không bao giờ biết Swing thực hiện cây của họ như thế nào, sự phụ thuộc của chúng là gì và điều này có thể thay đổi như thế nào trong tương lai. Swing không phải là một thư viện tiện ích mà là một thư viện UI.
jasop

44
Tôi nghĩ rằng thực hành mã hóa xấu là một chút khắc nghiệt.
Gareth Davis

51
javax.swing.tree.TreeModel là một giao diện công cộng (chính xác như java.util.List) và nó sẽ không có những thay đổi không tương thích. Một lợi thế bổ sung là bạn có thể dễ dàng gỡ lỗi / trực quan hóa cây của bạn trong khi phát triển.
lbalazscs

45

Cái này thì sao?

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
Làm thế nào DFS sẽ được thực hiện trên một cây được tạo bằng cách sử dụng đối tượng lớp này?
leba-lev

3
Làm thế nào để loại bỏ một lá được thực hiện bằng cách sử dụng lớp này?
ghedas

2
Trường đầu sẽ được sử dụng để làm gì?
Acour83

2
Sẽ thật tuyệt nếu lớp học này có một số tài liệu. Tôi hoàn toàn không hiểu phương pháp nào thích setAsParenthoặc getHeadlàm và đây là lúc tôi thực sự có thể nhận được sự giúp đỡ về cấu trúc dữ liệu cây. Ngay cả nguồn gốc của tài liệu không có ý kiến.
thảm họa

23

Tôi đã viết một thư viện nhỏ xử lý cây chung. Nó nhẹ hơn nhiều so với những thứ xoay. Tôi cũng có một dự án maven cho nó.


3
Tôi đang sử dụng nó bây giờ, hoạt động rực rỡ. Phải thay đổi nguồn đáng kể cho các tùy chỉnh của riêng tôi, nhưng đó là một điểm khởi đầu tuyệt vời. Cảm ơn Vivin!
jasop

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;
    }
}

Rõ ràng bạn có thể thêm các phương thức tiện ích để thêm / xóa con.


1
Điều này cho thấy rằng cha mẹ của cây là một cây. Tôi tin rằng bạn đã cố gắng tạo một lớp Tree Node.
Madhur Bhargava

16

Bạn nên bắt đầu bằng cách xác định cây là gì (đối với miền), điều này được thực hiện tốt nhất bằng cách xác định giao diện trước. Không phải tất cả các cấu trúc cây đều có thể sửa đổi, việc có thể thêmxóa các nút nên là một tính năng tùy chọn, vì vậy chúng tôi tạo một giao diện bổ sung cho điều đó.

Không cần phải tạo các đối tượng nút chứa các giá trị , trên thực tế tôi thấy đây là một lỗ hổng thiết kế chính và chi phí chung trong hầu hết các triển khai cây. Nếu bạn nhìn vào Swing, TreeModelthì không có lớp nút (chỉ DefaultTreeModelsử dụng TreeNode), vì chúng không thực sự cần thiết.

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

Cấu trúc cây có thể thay đổi (cho phép thêm và loại bỏ các nút):

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

Với các giao diện này, mã sử dụng cây không phải quan tâm nhiều đến cách cây được thực hiện. Điều này cho phép bạn sử dụng các triển khai chung cũng như các triển khai chuyên biệt , nơi bạn nhận ra cây bằng cách ủy thác các hàm cho API khác.

Ví dụ: cấu trúc cây tập tin

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();
    }
}

Ví dụ: cấu trúc cây chung (dựa trên quan hệ cha / con):

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
Tôi đang đối mặt với một vấn đề khi tôi theo cấu trúc này khi tôi thực hiện tree.add ("A", "B"); cây.add ("A", "C"); cây.add ("C", "D"); cây.add ("E", "A"); E là cha mẹ của A Chúng ta sẽ làm điều này như thế nào?
saNiks

1
Xin chào saNicks, có một lỗi trong đoạn mã trên khiến mối quan hệ cuối cùng không được thêm vào. Bây giờ nó đã được sửa và tôi cũng đã thêm các kiểm tra không null và (quan trọng hơn): kiểm tra theo chu kỳ ngăn chặn vi phạm cấu trúc cây (thêm mã hoặc một trong những tổ tiên của nó khi còn nhỏ). Cảm ơn các gợi ý!
Peter Walser

1
Tôi đã sửa lỗi nếu bất cứ ai đang tìm cách khắc phục lỗi đó, điều bạn phải làm là xem liệu phương thức add có trả về false không và nếu nó sai, chỉ cần tạo một temp LinkedHashset <N> mới và sao chép nút của cây vào đó thì bạn có thể xóa cây, thêm nút cha không được thêm vào trong bước trước và sau đó thêmTất cả tempNode trở lại cây cha mẹ ... Cảm ơn về cấu trúc mặc dù!
saNiks

2
Chỉ cần loại bỏ những sửa đổi công cộng vô dụng khỏi giao diện của bạn.
Hamedz

1
làm thế nào tôi có thể tạo ra một mảng json từ điều này
Pawan Pandey

12

Không có câu trả lời nào đề cập đến quá đơn giản nhưng mã làm việc, vì vậy đây là:

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

10

Bạn có thể sử dụng bất kỳ API XML nào của Java làm Tài liệu và Node..as XML là cấu trúc cây với Chuỗi


5
Ý tưởng tuyệt vời, Chúng tôi có thể sử dụng trong lược đồ XML bộ nhớ bằng cách sử dụng dom4j + jaxen xpath để tìm kiếm các nút.
Ben Rhouma Zied

8

Nếu bạn đang thực hiện mã hóa bảng trắng, một cuộc phỏng vấn hoặc thậm chí chỉ dự định sử dụng một cái cây, thì tính dài dòng của những thứ này là rất ít.

Cần phải nói thêm rằng lý do một cây không có trong đó như, nói Pair(về điều tương tự có thể được nói), là bởi vì bạn nên đóng gói dữ liệu của mình trong lớp bằng cách sử dụng nó cách thực hiện đơn giản nhất sẽ như sau:

/***
/* 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;
}

Đó thực sự là một cây chiều rộng tùy ý.

Nếu bạn muốn một cây nhị phân, nó thường dễ sử dụng hơn với các trường được đặt tên:

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

Hoặc nếu bạn muốn có một bộ ba:

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

Bây giờ bạn nói bạn muốn

để có thể nhận được tất cả các phần tử con (một số loại danh sách hoặc mảng của Chuỗi) được cung cấp một chuỗi đầu vào đại diện cho một nút đã cho

Nghe có vẻ như bài tập về nhà của bạn.
Nhưng vì tôi chắc chắn rằng bất kỳ thời hạn nào đã trôi qua

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()));
 }
}

Điều này giúp bạn sử dụng như:

$ 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

Cùng dòng với câu trả lời của Gareth, hãy xem DefaultMutableTreeNode . Nó không chung chung, nhưng có vẻ phù hợp với dự luật. Mặc dù trong gói javax.swing, nó không phụ thuộc vào bất kỳ lớp AWT hoặc Swing nào. Trong thực tế, mã nguồn thực sự có bình luận// ISSUE: this class depends on nothing in AWT -- move to java.util?


Lol, làm thế nào bạn thậm chí đã đi qua lớp này?
Pacerier

7

Có một vài cấu trúc dữ liệu cây trong Java, chẳng hạn như DefaultMutableTreeNode trong JDK Swing, gói trình phân tích cú pháp Tree trong Stanford và các mã đồ chơi khác. Nhưng không ai trong số này là đủ nhưng đủ nhỏ cho mục đích chung.

Dự án cây Java cố gắng cung cấp một cấu trúc dữ liệu cây đa năng khác trong Java. Sự khác biệt giữa cái này và cái khác là

  • Hoàn toàn miễn phí. Bạn có thể sử dụng nó ở bất cứ đâu (ngoại trừ trong bài tập về nhà của bạn: P)
  • Nhỏ nhưng đủ chung. Tôi đặt mọi thứ của cấu trúc dữ liệu trong một tệp lớp, vì vậy nó sẽ dễ dàng sao chép / dán.
  • Không chỉ là một món đồ chơi. Tôi nhận thức được hàng tá mã cây Java chỉ có thể xử lý các cây nhị phân hoặc các hoạt động hạn chế. TreeNode này là nhiều hơn thế. Nó cung cấp các cách khác nhau để truy cập các nút, chẳng hạn như preorder, postorder, Breadthfirst, leaf, path to root, v.v.
  • Nhiều tiện ích sẽ được thêm vào. Tôi sẵn sàng thêm nhiều hoạt động để làm cho dự án này toàn diện, đặc biệt nếu bạn gửi yêu cầu qua github.


5

Vì câu hỏi yêu cầu cấu trúc dữ liệu có sẵn, nên một cây có thể được xây dựng từ danh sách hoặc mảng:

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

instanceof có thể được sử dụng để xác định xem một phần tử là cây con hay nút cuối.


2
Khá xấu xí. Và không hoạt động, nếu các đối tượng dữ liệu của bạn có thể là các mảng tương ứng liệt kê.
dùng686249

1
Tôi đồng ý rằng nó xấu. Các Objects sẽ là các đối tượng lá (ví dụ: Strings) hoặc các nhánh (được biểu thị bằng các mảng). Và nó hoạt động: mã đó sẽ biên dịch và nó tạo ra một cây nhỏ Strings.
Oledit

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

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

Đơn giản như nó được và rất dễ sử dụng. Để sử dụng nó, mở rộng nó:

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

3

Ví dụ :

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

Trong quá khứ tôi chỉ sử dụng một bản đồ lồng nhau cho việc này. Đây là những gì tôi sử dụng ngày nay, nó rất đơn giản nhưng nó phù hợp với nhu cầu của tôi. Có lẽ điều này sẽ giúp một người khác.

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

Tôi đã viết một lớp "TreeMap" nhỏ dựa trên "HashMap" hỗ trợ thêm đường dẫn:

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);
    }

}

Nó có thể được sử dụng để lưu trữ một Cây thuộc loại "T" (chung chung), nhưng không (chưa) hỗ trợ lưu trữ dữ liệu bổ sung trong các nút của nó. Nếu bạn có một tập tin như thế này:

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

Sau đó, bạn có thể làm cho nó một cây bằng cách thực hiện:

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

Và bạn sẽ có được một cái cây đẹp. Nó phải dễ dàng để thích ứng với nhu cầu của bạn.


2

Bạn có thể sử dụng lớp HashTree có trong Apache JMeter, một phần của Dự án Jakarta.

Lớp HashTree được bao gồm trong gói org.apache.jorphan.collections. Mặc dù gói này không được phát hành bên ngoài dự án JMeter, bạn có thể dễ dàng lấy nó:

1) Tải về các nguồn JMeter .

2) Tạo một gói mới.

3) Sao chép vào nó / src / jorphan / org / apache / jorphan / sưu tập /. Tất cả các tệp ngoại trừ Data.java

4) Sao chép cũng /src/jorphan/org/apache/jorphan/util/JOrphanUtils.java

5) HashTree đã sẵn sàng để sử dụng.


2

Không có cấu trúc dữ liệu cụ thể trong Java phù hợp với yêu cầu của bạn. Yêu cầu của bạn khá cụ thể và bạn cần phải thiết kế cấu trúc dữ liệu của riêng mình. Nhìn vào yêu cầu của bạn, bất cứ ai cũng có thể nói rằng bạn cần một loại cây n-ary với một số chức năng cụ thể. Bạn có thể thiết kế cấu trúc dữ liệu của mình theo cách sau:

  1. Cấu trúc của nút của cây sẽ giống như nội dung trong nút và danh sách các phần tử con như: class Node {String value; Liệt kê trẻ em;}
  2. Bạn cần truy xuất các phần tử con của một chuỗi đã cho, để bạn có thể có 2 phương thức 1: Node searchNode (Chuỗi str), sẽ trả về nút có cùng giá trị như đầu vào đã cho (sử dụng BFS để tìm kiếm) 2: Liệt kê getChildren (Chuỗi str): phương thức này sẽ gọi bên trong searchNode để lấy nút có cùng chuỗi và sau đó nó sẽ tạo danh sách tất cả các giá trị chuỗi của con và trả về.
  3. Bạn cũng sẽ được yêu cầu chèn một chuỗi trong cây. Bạn sẽ phải viết một phương thức nói void insert (Chuỗi cha, giá trị chuỗi): điều này sẽ một lần nữa tìm kiếm nút có giá trị bằng cha và sau đó bạn có thể tạo một Nút có giá trị đã cho và thêm vào danh sách con cho cha mẹ tìm thấy .

Tôi muốn đề nghị, bạn viết cấu trúc của nút trong một lớp như Class Node {String value; Liệt kê các hàm con {// thực hiện BFS và trả về Node}


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
Xin đừng chỉ đổ mã - giải thích những gì nó làm, và đặc biệt là tại sao nó khác (tốt hơn) so với tất cả các câu trả lời khác.
Jan Doggen

2

Tôi đã viết một thư viện cây chơi độc đáo với Java8 và không có phụ thuộc nào khác. Nó cũng cung cấp một sự giải thích lỏng lẻo về một số ý tưởng từ lập trình chức năng và cho phép bạn ánh xạ / bộ lọc / cắt tỉa / tìm kiếm toàn bộ cây hoặc cây con.

https://github.com/RutledgePaulV/prune

Việc triển khai không làm gì đặc biệt với việc lập chỉ mục và tôi đã không đi lạc khỏi đệ quy, vì vậy có thể với hiệu suất cây lớn sẽ giảm và bạn có thể thổi bay ngăn xếp. Nhưng nếu tất cả những gì bạn cần là một cây đơn giản có độ sâu nhỏ đến trung bình, tôi nghĩ nó hoạt động đủ tốt. Nó cung cấp một định nghĩa lành mạnh (dựa trên giá trị) của đẳng thức và nó cũng có một triển khai toString cho phép bạn hình dung cây!


1

Vui lòng kiểm tra mã dưới đây, nơi tôi đã sử dụng các cấu trúc dữ liệu Tree, mà không sử dụng các lớp Collection. Mã có thể có lỗi / cải tiến nhưng vui lòng sử dụng mã này chỉ để tham khảo

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
"Không sử dụng các lớp Collection " Ah? Vậy lớp Queue đến từ đâu? Và như đã nói ở trên, nó là một cây nhị phân, thất bại ở yêu cầu đầu tiên (bất kỳ số nút con nào).
PhiLho

1

Bạn có thể sử dụng lớp TreeSet trong java.util. *. Nó hoạt động như cây tìm kiếm nhị phân, vì vậy nó đã được sắp xếp. Lớp TreeSet thực hiện các giao diện Iterable, Collection và Set. Bạn có thể duyệt qua cây bằng iterator như một bộ.

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

Bạn có thể kiểm tra, Java Doc và một số thứ khác .


-1

Cây tùy chỉnh thực hiện Tree mà không sử dụng khung Collection. Nó chứa các hoạt động cơ bản khác nhau cần thiết trong việc thực hiện 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);             
    }

}

11
Đó là cây nhị phân, nó thất bại ở yêu cầu đầu tiên của OP ...
PhiLho
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.