วิธีการใช้โครงสร้างข้อมูลทรีใน Java [ปิด]


496

มีคลาสไลบรารี Java มาตรฐานใด ๆ ที่แสดงถึงต้นไม้ใน Java หรือไม่?

โดยเฉพาะฉันต้องแสดงต่อไปนี้:

  • ต้นไม้ย่อยที่โหนดใด ๆ สามารถมีจำนวนลูกโดยพลการ
  • แต่ละโหนด (หลังจากรูท) และลูกนั้นจะมีค่าสตริง
  • ฉันต้องการรับ children ทั้งหมด (เรียงลำดับรายการหรือ array of Strings บางส่วน) ของโหนดที่กำหนดและเป็นค่าสตริง (เช่นวิธีที่จะใช้โหนดเป็นอินพุตและส่งกลับค่าสตริงทั้งหมดของโหนดชายด์เป็นเอาต์พุต)

มีโครงสร้างที่พร้อมใช้งานสำหรับสิ่งนี้หรือฉันต้องการสร้างของฉันเอง (ถ้าเป็นเช่นนั้น


3
หากคุณใช้ Java 8 และต้องการสำรวจโหนดของคุณด้วยสตรีมการกรอง ฯลฯ จากนั้นคุณอาจต้องการดูทุเรียนgithub.com/diffplug/durian
เน็ด Twigg

1
คุณสามารถใช้ API นี้: sourceforge.net/p/treeds4j
Mohamed Ennahdi El Idrissi

คำตอบ:


306

ที่นี่:

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หรือวัตถุอื่น ๆ มันค่อนข้างง่ายที่จะใช้ต้นไม้ง่าย ๆ เพื่อทำสิ่งที่คุณต้องการ

สิ่งที่คุณต้องเพิ่มคือวิธีการเพิ่มการลบออกจากการสำรวจและการสร้าง เป็นอาคารบล็อกพื้นฐานของNodeTree


304
Treeไม่จำเป็นต้องพูดอย่างเคร่งครัดในชั้นเรียนเพราะทุกคนNodeในตัวมันสามารถถูกมองว่าเป็นต้นไม้
Joachim Sauer

34
@ โจวฉันชอบมีโครงสร้างที่จะมีราก คุณสามารถเพิ่มวิธีการในคลาสทรีที่เหมาะสมกว่าในการเรียกใช้ทรีมากกว่าโหนดเดียว
jjnguy

24
@Justin: ยกตัวอย่างเช่น? นั่นเป็นคำถามที่ตรงไปตรงมา: ฉันไม่สามารถนึกถึงวิธีการเดียวที่ทำให้ต้นไม้ทั้งต้นไม่เข้าใจในต้นไม้ย่อย
โจอาคิมซาวเออร์

22
ฉันเห็นด้วยกับ @Joa ว่าไม่จำเป็นต้องใช้คลาสของทรี ฉันชอบที่จะปล่อยให้ธรรมชาติของต้นไม้แบบวนซ้ำโดยชัดเจนในโค้ดโดยไม่ต้องเพิ่มคลาส Tree แยกต่างหากและใช้คลาส Node อย่างสม่ำเสมอ แต่ฉันตั้งชื่อตัวแปร 'tree' หรือ 'root' ถ้าต้องการให้ชัดเจนว่าคุณกำลังจัดการกับโหนด Node ของต้นไม้
jvdbogae

89
@Joa อายุสี่ปีขึ้นไปฉันเห็นด้วยกับคุณอย่างสมบูรณ์ ระวังชั้นต้นไม้
jjnguy

122

อีกโครงสร้างของต้นไม้:

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

โบนัส
ดูต้นไม้ที่เต็มไปด้วย:

  • iterator
  • ค้นหา
  • Java / C #

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


2
เพิ่งพบว่าห้องสมุดของคุณมีประโยชน์อย่างยิ่ง ขอบคุณ. แต่ฉันอยากจะรู้วิธีการนำโครงสร้างต้นไม้แบบไดนามิกมาใช้โดยอ้างอิงความสัมพันธ์ระหว่างผู้ปกครองกับลูก ตัวอย่างที่ได้รับคือฉันมีสมาชิกหนึ่งคนเป็นสปอนเซอร์อีกหนึ่งคนและสมาชิกอีก 2 คนเป็นสปอนเซอร์สมาชิกคนที่ 3 และอื่น ๆ มีตารางบันทึกความสัมพันธ์อยู่แล้ว แต่ไม่แน่ใจว่าฉันสามารถเติมลงในแผนภูมิโดยใช้ห้องสมุดของคุณ
d4v1dv00

1
ตั้งแต่ปี 2559 ลิงก์ไม่มีไฟล์หรือไฟล์ต้นฉบับ
Sharon Ben Asher

2
ในมุมมองของฉันคำตอบนี้สามปีหลังจากคำตอบที่ได้รับคะแนนสูงกว่าข้างต้นคือคำตอบที่สะอาดกว่า อย่างไรก็ตามฉันจะแทนที่ LinkedList ด้วย ArrayList สำหรับ this.children
HopeKing

1
ฉันจะใช้ชุดสำหรับเด็ก ๆ
DPM

ฉันอาจผิด แต่ปรากฏว่าเมื่อใช้งานนี้คุณต้องโทรhasNext()ก่อนการโทรแต่ละครั้งnext()เพื่อรับผลลัพธ์ที่ถูกต้อง นี่ไม่ใช่ส่วนหนึ่งของIteratorข้อมูลจำเพาะ
Robert Lewis

97

จริงๆแล้วมีโครงสร้างต้นไม้ที่ดีพอสมควรใน JDK

มีลักษณะที่javax.swing.tree , TreeModelและTreeNode พวกเขาถูกออกแบบมาให้ใช้กับJTreePanelแต่ในความเป็นจริงการใช้งานต้นไม้ที่ดีและไม่มีอะไรหยุดคุณจากการใช้มันโดยไม่ต้องใช้อินเตอร์เฟสแบบสวิง

หมายเหตุว่าเป็นของ Java 9 คุณอาจต้องการที่จะไม่ใช้ชั้นเรียนเหล่านี้ที่พวกเขาจะไม่ได้อยู่ในโปรไฟล์กระชับ '


5
ใช่ฉันเคยใช้พวกเขาในอดีตและพวกเขาจะทำทุกอย่างที่คุณต้องการจากต้นไม้ ข้อเสียเดียวที่ฉันนึกได้คือความยาวของชื่อของคลาสการใช้งานที่เกี่ยวข้อง: DefaultTreeModel และ DefaultMutableTreeNode Verbose แต่ไม่ใช่ทั้งหมดที่สำคัญฉันเดา
Gobblement ขั้นสูงสุด

4
วิธีที่ดีในการจัดการกับวิธีนี้คือการสร้างเมท็อดแบบคงที่สองวิธี newModel () และ newNode () จากนั้นนำเข้าสแตติก
Gareth Davis

140
ฉันจะหลีกเลี่ยงการใช้ห้องสมุดแกว่งในฟังก์ชั่นที่ไม่เกี่ยวข้องกับการแกว่ง นี่คือการปฏิบัติที่ไม่ดีการเข้ารหัส คุณไม่เคยรู้เลยว่า Swing นำต้นไม้มาใช้อย่างไรการพึ่งพาของพวกเขาคืออะไรและมันจะเปลี่ยนแปลงได้อย่างไรในอนาคต Swing ไม่ใช่ไลบรารียูทิลิตี้ แต่เป็นไลบรารี UI
jasop

44
ฉันคิดว่าการฝึกเขียนโค้ดที่ไม่ดีนั้นค่อนข้างรุนแรง
Gareth Davis

51
javax.swing.tree.TreeModel เป็นอินเตอร์เฟสสาธารณะ (เหมือนกับ java.util.List) และจะไม่มีการเปลี่ยนแปลงที่เข้ากันไม่ได้ ข้อได้เปรียบที่เพิ่มเข้ามาคือคุณสามารถดีบัก / แสดงภาพต้นไม้ของคุณในขณะที่กำลังพัฒนา
lbalazscs

45

แล้วเรื่องนี้ล่ะ

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
DFS จะถูกนำไปใช้กับต้นไม้ที่สร้างขึ้นโดยใช้วัตถุคลาสนี้ได้อย่างไร
leba-lev

3
การลบใบไม้จะถูกนำไปใช้งานโดยใช้คลาสนี้ได้อย่างไร
ghedas

2
ฟิลด์ส่วนหัวจะใช้ทำอะไร
Ac8383

2
คงจะดีมากถ้าคลาสนี้มีเอกสารประกอบ ฉันไม่เข้าใจวิธีการที่ชอบsetAsParentหรือgetHeadทำและนี่คือเวลาที่ฉันจะได้รับความช่วยเหลือเกี่ยวกับโครงสร้างข้อมูลต้นไม้ แม้แต่ต้นฉบับดั้งเดิมของเอกสารก็ยังไม่มีความคิดเห็น
disasterkid

23

ฉันเขียนห้องสมุดเล็ก ๆ ที่จัดการกับต้นไม้ทั่วไป มันเบากว่าของสวิงมาก ฉันยังมีโครงการ mavenสำหรับมัน


3
ฉันใช้มันตอนนี้ใช้งานได้ดี ต้องเปลี่ยนแหล่งที่มาอย่างมีนัยสำคัญสำหรับการปรับแต่งของฉันเอง แต่มันเป็นจุดเริ่มต้นที่ดี ขอบคุณ 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;
    }
}

เห็นได้ชัดว่าคุณสามารถเพิ่มวิธีการยูทิลิตี้เพื่อเพิ่ม / ลบเด็ก ๆ


1
สิ่งนี้ชี้ให้เห็นว่าต้นกำเนิดของต้นไม้เป็นต้นไม้ ฉันเชื่อว่าคุณกำลังพยายามสร้างคลาส Tree Node
Madhur Bhargava

16

คุณควรเริ่มด้วยการกำหนดว่าทรีคืออะไร (สำหรับโดเมน) สิ่งนี้ทำได้ดีที่สุดโดยการกำหนดอินเทอร์เฟซก่อน ไม่สามารถปรับเปลี่ยนโครงสร้างต้นไม้ได้ทั้งหมดสามารถเพิ่มและลบได้โหนดควรเป็นคุณสมบัติเพิ่มเติมดังนั้นเราจึงสร้างอินเทอร์เฟซพิเศษสำหรับสิ่งนั้น

ไม่จำเป็นต้องสร้างวัตถุโหนดที่เก็บค่าในความเป็นจริงฉันเห็นนี้เป็นข้อบกพร่องการออกแบบที่สำคัญและค่าใช้จ่ายในการใช้งานต้นไม้ส่วนใหญ่ หากคุณดูที่ 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);
        }
    }
}

1
ฉันกำลังเผชิญปัญหาเมื่อฉันทำตามโครงสร้างนี้เมื่อฉันทำ tree.add ("A", "B"); tree.add ("A", "C"); tree.add ("C", "D"); tree.add ("E", "A"); E เป็นผู้ปกครองของ A เราจะทำสิ่งนี้ได้อย่างไร
saNiks

1
สวัสดี saNicks มีข้อผิดพลาดในโค้ดด้านบนซึ่งทำให้ความสัมพันธ์ครั้งสุดท้ายไม่ถูกเพิ่ม ตอนนี้ได้รับการแก้ไขแล้วและฉันยังเพิ่มการตรวจสอบที่ไม่เป็นโมฆะและ (สำคัญกว่า): การตรวจสอบแบบวนซึ่งช่วยป้องกันการละเมิดโครงสร้างต้นไม้ ขอบคุณสำหรับคำใบ้!
Peter Walser

1
ฉันแก้ไขข้อผิดพลาดหากใครกำลังมองหาการแก้ไขข้อผิดพลาดนั้นสิ่งที่คุณต้องทำคือการดูว่าการเพิ่มวิธีการส่งกลับเท็จและถ้ามันเป็นเท็จเพียงแค่สร้าง temp ใหม่ LinkedHashSet <N> และโคลนต้นไม้ nodelist ลงไปแล้ว ต้นไม้เพิ่มโหนดแม่ซึ่งไม่ได้รับการเพิ่มในขั้นตอนก่อนหน้าแล้วเพิ่มทั้งหมดของ tempNode กลับไปที่ต้นไม้แม่ ... ขอบคุณสำหรับโครงสร้างแม้ว่า!
saNiks

2
เพียงลบตัวดัดแปลงสาธารณะที่ไม่มีประโยชน์ออกจากอินเทอร์เฟซของคุณ
Hamedz

1
ฉันจะสร้างอาร์เรย์ json จากสิ่งนี้ได้อย่างไร
Pawan Pandey

12

ไม่มีคำตอบที่กล่าวถึงรหัสที่ซับซ้อนเกินไป แต่ใช้งานได้ดังนั้นนี่คือ:

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

10

คุณสามารถใช้ XML API ของ Java เป็น Document และ Node .. as XML เป็นโครงสร้างแบบต้นไม้ที่มี Strings


5
ความคิดที่ยอดเยี่ยมเราสามารถใช้ใน XML schema ของหน่วยความจำโดยใช้ dom4j + jaxen xpath เพื่อค้นหาโหนด
Ben Rhouma Zied

8

หากคุณกำลังเขียนโค้ดไวท์บอร์ดการสัมภาษณ์หรือเพียงแค่วางแผนที่จะใช้ต้นไม้ความฟุ่มเฟื่อยของสิ่งเหล่านี้มีน้อยมาก

มันควรจะกล่าวต่อไปว่าเหตุผลที่ต้นไม้ไม่อยู่ในนั้นเช่นพูด 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
[]

7

พร้อมสายเดียวกับคำตอบของกาเร็ ธ ตรวจสอบDefaultMutableTreeNode มันไม่ใช่เรื่องทั่วไป แต่อย่างอื่นดูเหมือนจะพอดีกับใบเสร็จ แม้ว่าจะอยู่ในแพ็คเกจ javax.swing แต่ก็ไม่ได้ขึ้นอยู่กับคลาส AWT หรือ Swing ใด ๆ ในความเป็นจริงรหัสแหล่งที่มาจริงมีความคิดเห็น// ISSUE: this class depends on nothing in AWT -- move to java.util?


ฮ่า ๆ เจ้ามาถึงชั้นนี้ได้อย่างไร?
Pacerier

7

มีโครงสร้างข้อมูลต้นไม้สองสามอย่างใน Java เช่น DefaultMutableTreeNode ใน JDK Swing, Tree ในแพคเกจตัวแยกวิเคราะห์ Stanford และรหัสของเล่นอื่น ๆ แต่สิ่งเหล่านี้ไม่เพียงพอ แต่เล็กพอสำหรับวัตถุประสงค์ทั่วไป

โปรเจ็กต์Java-treeพยายามจัดเตรียมโครงสร้างข้อมูลทรีทั่วไปอื่นใน Java ความแตกต่างระหว่างสิ่งนี้กับคนอื่นคือ

  • ฟรีทั้งหมด คุณสามารถใช้งานได้ทุกที่ (ยกเว้นในการทำการบ้านของคุณ: P)
  • เล็ก แต่กว้างพอสมควร ฉันใส่ทุกอย่างของโครงสร้างข้อมูลไว้ในไฟล์คลาสเดียวดังนั้นจึงง่ายที่จะคัดลอก / วาง
  • ไม่ใช่แค่ของเล่น ฉันรู้รหัส Java tree ที่สามารถจัดการกับ binary tree หรือการดำเนินการที่ จำกัด ได้เท่านั้น TreeNode นี้มีมากกว่านั้น มันมีวิธีการที่แตกต่างกันของการเยี่ยมชมโหนดเช่น preorder, postorder, breadthfirst, ใบไม้, เส้นทางไปยังรากและอื่น ๆ นอกจากนี้ยังมีการทำซ้ำสำหรับความพอเพียง
  • อุปกรณ์เพิ่มเติมจะถูกเพิ่มเข้ามา ฉันยินดีที่จะเพิ่มการดำเนินการเพิ่มเติมเพื่อให้โครงการนี้ครอบคลุมโดยเฉพาะถ้าคุณส่งคำขอผ่าน GitHub


5

เนื่องจากคำถามถามโครงสร้างข้อมูลที่มีอยู่ต้นไม้จึงสามารถสร้างได้จากรายการหรืออาร์เรย์:

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

instanceof สามารถใช้เพื่อตรวจสอบว่าองค์ประกอบเป็นทรีย่อยหรือโหนดขั้ว


2
ค่อนข้างน่าเกลียด และไม่ทำงานหากวัตถุข้อมูลของคุณอาจเป็นรายการลำดับตามลำดับ
user686249

1
ฉันยอมรับว่ามันน่าเกลียด Objects อย่างใดอย่างหนึ่งจะเป็นวัตถุใบ (ตัวอย่างเช่นStringS) หรือสาขา (แสดงโดยอาร์เรย์) และมันไม่ทำงาน: ว่ารหัสจะรวบรวมและมันจะสร้างต้นไม้ขนาดเล็กของStrings
Olathe

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

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

ง่ายเหมือนที่ได้รับและใช้งานง่ายมาก หากต้องการใช้ให้ขยาย:

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

3

ตัวอย่างเช่น :

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

ในอดีตที่ผ่านมาฉันเพิ่งใช้แผนที่ซ้อนกันสำหรับเรื่องนี้ นี่คือสิ่งที่ฉันใช้ในวันนี้มันง่ายมาก แต่ก็เหมาะกับความต้องการของฉัน บางทีนี่อาจช่วยได้อีก

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

ฉันเขียนคลาส "TreeMap" ขนาดเล็กตาม "HashMap" ที่รองรับการเพิ่มพา ธ :

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

และคุณจะได้รับต้นไม้ที่ดี ควรปรับให้เข้ากับความต้องการของคุณได้ง่าย


2

คุณสามารถใช้HashTreeคลาสรวมอยู่ใน Apache JMeter ซึ่งเป็นส่วนหนึ่งของโครงการจาการ์ตา

คลาส HashTree รวมอยู่ในแพ็คเกจ org.apache.jorphan.collections แม้ว่าแพ็คเกจนี้จะไม่ออกนอกโครงการ JMeter คุณสามารถรับได้ง่าย:

1) ดาวน์โหลดแหล่งข้อมูล JMeterแหล่ง

2) สร้างแพ็คเกจใหม่

3) คัดลอกมัน / src / jorphan / org / apache / jorphan / collection / ไฟล์ทั้งหมดยกเว้น Data.java

4) คัดลอกด้วย /src/jorphan/org/apache/jorphan/util/JOrphanUtils.java

5) HashTree พร้อมใช้งาน


2

ไม่มีโครงสร้างข้อมูลเฉพาะใน Java ที่เหมาะสมกับความต้องการของคุณ ความต้องการของคุณค่อนข้างเฉพาะเจาะจงและคุณจำเป็นต้องออกแบบโครงสร้างข้อมูลของคุณเอง ดูที่ความต้องการของคุณทุกคนสามารถพูดได้ว่าคุณต้องการต้นไม้ n-ary บางชนิดที่มีฟังก์ชั่นเฉพาะบางอย่าง คุณสามารถออกแบบโครงสร้างข้อมูลด้วยวิธีต่อไปนี้:

  1. โครงสร้างของโหนดของต้นไม้จะเป็นเหมือนเนื้อหาในโหนดและรายชื่อลูกที่ชอบ: โหนดคลาส {ค่าสตริง; รายชื่อลูก ๆ ;}
  2. คุณต้องดึงข้อมูลลูกของสตริงที่กำหนดเพื่อให้คุณมี 2 วิธี 1: Node searchNode (String str) จะส่งคืนโหนดที่มีค่าเหมือนกับอินพุตที่กำหนด (ใช้ BFS สำหรับการค้นหา) 2: รายการ getChildren (String str): เมธอดนี้จะเรียก searchNode ภายในเพื่อให้โหนดมีสตริงเหมือนกันจากนั้นจะสร้างรายการของค่าสตริงทั้งหมดของ children และ return
  3. คุณจะต้องใส่สตริงในทรีด้วย คุณจะต้องเขียนวิธีหนึ่งที่บอกว่า void insert (String parent, String value): นี่จะค้นหาโหนดที่มีค่าเท่ากับ parent และจากนั้นคุณสามารถสร้าง Node ด้วยค่าที่กำหนดและเพิ่มลงในรายการของเด็ก ๆ ไปยัง parent ที่พบ .

ฉันอยากจะแนะนำให้คุณเขียนโครงสร้างของโหนดในหนึ่งคลาสเช่นโหนดคลาส {ค่าสตริง; แสดงรายการ children;} และวิธีอื่น ๆ ทั้งหมดเช่นการค้นหาการแทรกและ getChildren ในคลาส NodeUtils อื่นเพื่อให้คุณสามารถส่งผ่านรูทของต้นไม้เพื่อดำเนินการบนต้นไม้เฉพาะเช่น: คลาส NodeUtils {การค้นหาโหนดคงที่สาธารณะ (รูตโหนดค่าสตริง) {// ดำเนินการ BFS และส่งคืนโหนด}


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
โปรดอย่าเพิ่งถ่ายโอนรหัส - อธิบายว่ามันทำอะไรและโดยเฉพาะอย่างยิ่งทำไมมันถึงแตกต่าง (ดีกว่า) กว่าคำตอบอื่น ๆ ทั้งหมด
ม.ค. Doggen

2

ฉันเขียนไลบรารีต้นไม้ที่เล่นกับ Java8 ได้ดีและไม่มีการอ้างอิงอื่น นอกจากนี้ยังมีการตีความแนวคิดบางอย่างที่ผิดเพี้ยนจากการตั้งโปรแกรมการทำงานและให้คุณแมป / ตัวกรอง / ลูกพรุน / ค้นหาทั้งต้นไม้หรือต้นไม้ย่อย

https://github.com/RutledgePaulV/prune

การนำไปใช้ไม่ได้ทำอะไรเป็นพิเศษกับการจัดทำดัชนีและฉันไม่ได้หลงทางจากการเรียกซ้ำดังนั้นจึงเป็นไปได้ที่ประสิทธิภาพของต้นไม้ขนาดใหญ่จะลดลงและคุณสามารถระเบิดกองซ้อนได้ แต่ถ้าสิ่งที่คุณต้องการคือต้นไม้ที่ตรงไปตรงมาที่มีความลึกน้อยถึงปานกลางฉันคิดว่ามันใช้ได้ดีพอ มันให้คำจำกัดความที่มีเหตุผล (ตามค่า) ของความเท่าเทียมและยังมีการติดตั้ง toString ที่ช่วยให้คุณเห็นภาพต้นไม้!


1

โปรดตรวจสอบรหัสด้านล่างที่ฉันใช้โครงสร้างข้อมูลแบบต้นไม้โดยไม่ต้องใช้คลาสการรวบรวม รหัสอาจมีข้อบกพร่อง / การปรับปรุง แต่โปรดใช้เพื่อการอ้างอิงเท่านั้น

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
" โดยไม่ใช้คลาสของคอลเล็กชัน " อ่า แล้วคลาสคิวมาจากไหน และดังที่ได้กล่าวไว้ข้างต้นมันเป็นต้นไม้ไบนารีล้มเหลวที่ความต้องการแรก (โหนดลูกจำนวนใด ๆ )
PhiLho

1

คุณสามารถใช้คลาส TreeSet ใน java.util. * มันทำงานเหมือนแผนภูมิการค้นหาแบบไบนารี่ดังนั้นจึงถูกเรียงลำดับแล้ว คลาส TreeSet ใช้อินเตอร์เฟส Iterable, Collection และ Set คุณสามารถท่องไปตามต้นไม้ด้วยตัววนซ้ำเหมือนชุด

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

คุณสามารถตรวจสอบ, Java หมอและบางส่วนอื่น ๆ


-1

Tree แบบกำหนดเองใช้ Tree โดยไม่ใช้ Framework Framework มันมีการดำเนินงานพื้นฐานที่แตกต่างกันที่จำเป็นในการใช้งานต้นไม้

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
ที่ต้นไม้ไบนารีก็ล้มเหลวที่ต้องการแรกของสหกรณ์ ...
PhiLho
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.