Java中的树实现(根,父母和孩子)

Posted

技术标签:

【中文标题】Java中的树实现(根,父母和孩子)【英文标题】:Tree implementation in Java (root, parents and children) 【发布时间】:2013-10-12 05:03:46 【问题描述】:

我需要创建一个类似于 Java 中附加图像的树结构。我发现了一些与此相关的问题,但我还没有找到令人信服且解释清楚的回答。 应用业务包括食品超级品类(主菜、甜品等)。这些类别中的每一个都可以有父项或子项等。

【问题讨论】:

【参考方案1】:
import java.util.ArrayList;
import java.util.List;

public class Node<T> 
    private List<Node<T>> children = new ArrayList<Node<T>>();
    private Node<T> parent = null;
    private T data = null;

    public Node(T data) 
        this.data = data;
    

    public Node(T data, Node<T> parent) 
        this.data = data;
        this.parent = parent;
    

    public List<Node<T>> getChildren() 
        return children;
    

    public void setParent(Node<T> parent) 
        parent.addChild(this);
        this.parent = parent;
    

    public void addChild(T data) 
        Node<T> child = new Node<T>(data);
        child.setParent(this);
        this.children.add(child);
    

    public void addChild(Node<T> child) 
        child.setParent(this);
        this.children.add(child);
    

    public T getData() 
        return this.data;
    

    public void setData(T data) 
        this.data = data;
    

    public boolean isRoot() 
        return (this.parent == null);
    

    public boolean isLeaf() 
        return this.children.size == 0;
    

    public void removeParent() 
        this.parent = null;
    

例子:

import java.util.List;

Node<String> parentNode = new Node<String>("Parent"); 
Node<String> childNode1 = new Node<String>("Child 1", parentNode);
Node<String> childNode2 = new Node<String>("Child 2");     

childNode2.setParent(parentNode); 

Node<String> grandchildNode = new Node<String>("Grandchild of parentNode. Child of childNode1", childNode1); 
List<Node<String>> childrenNodes = parentNode.getChildren();

【讨论】:

这个好,请问有树函数的使用例子吗? Node&lt;String&gt; parentNode = new Node&lt;String&gt;("Parent"); Node&lt;String&gt; childNode1 = new Node&lt;String&gt;("Child 1", parentNode); Node&lt;String&gt; childNode2 = new Node&lt;String&gt;("Child 2"); childNode2.setParent(parentNode); Node&lt;String&gt; grandchildNode = new Node&lt;String&gt;("Grandchild of parentNode. Child of childNode1", childNode1); List&lt;Node&lt;String&gt;&gt; childrenNodes = parentNode.getChildren(); java.lang.***Error at tree.Node.addChild(Node.java:40) at tree.Node.setParent(Node.java:29) at tree.Node.addChild(Node.java: 40) 在 tree.Node.setParent(Node.java:29) 在 tree.Node.addChild(Node.java:40) 在 tree.Node.setParent(Node.java:29) 在 tree.Node.addChild(Node. java:40) 在 tree.Node.setParent(Node.java:29) 在 tree.Node.addChild(Node.java:40) 在 tree.Node.setParent(Node.java:29) 在 tree.Node.addChild( Node.java:40) 在 tree.Node.setParent(Node.java:29) 应该有equalshashCode方法吗? 这段代码还抛出***Error真的很奇怪【参考方案2】:

Accepted answer 在调用setParentaddChild 方法时抛出java.lang.***Error

这是一个没有这些错误的稍微简单的实现:

public class MyTreeNode<T>
    private T data = null;
    private List<MyTreeNode> children = new ArrayList<>();
    private MyTreeNode parent = null;

    public MyTreeNode(T data) 
        this.data = data;
    

    public void addChild(MyTreeNode child) 
        child.setParent(this);
        this.children.add(child);
    

    public void addChild(T data) 
        MyTreeNode<T> newChild = new MyTreeNode<>(data);
        this.addChild(newChild);
    

    public void addChildren(List<MyTreeNode> children) 
        for(MyTreeNode t : children) 
            t.setParent(this);
        
        this.children.addAll(children);
    

    public List<MyTreeNode> getChildren() 
        return children;
    

    public T getData() 
        return data;
    

    public void setData(T data) 
        this.data = data;
    

    private void setParent(MyTreeNode parent) 
        this.parent = parent;
    

    public MyTreeNode getParent() 
        return parent;
    

一些例子:

MyTreeNode<String> root = new MyTreeNode<>("Root");

MyTreeNode<String> child1 = new MyTreeNode<>("Child1");
child1.addChild("Grandchild1");
child1.addChild("Grandchild2");

MyTreeNode<String> child2 = new MyTreeNode<>("Child2");
child2.addChild("Grandchild3");

root.addChild(child1);
root.addChild(child2);
root.addChild("Child3");

root.addChildren(Arrays.asList(
        new MyTreeNode<>("Child4"),
        new MyTreeNode<>("Child5"),
        new MyTreeNode<>("Child6")
));

for(MyTreeNode node : root.getChildren()) 
    System.out.println(node.getData());

【讨论】:

这种设计存在循环依赖和可变性。可以通过取消父节点并在需要时将其放入到父节点的单独节点映射中来解决。 myNode.addChild(myNode) > 循环引用 > 不再是一棵树了【参考方案3】:

这是我根据您的要求在 java 中的实现。 在 treeNode 类中,我使用 generic array 来存储树数据。我们还可以使用 arraylist动态数组 来存储树的值。

public class TreeNode<T> 
   private T value = null;
   private TreeNode[] childrens = new TreeNode[100];
   private int childCount = 0;

    TreeNode(T value) 
        this.value = value;
    

    public TreeNode addChild(T value) 
        TreeNode newChild = new TreeNode(value, this);
        childrens[childCount++] = newChild;
        return newChild;
    

    static void traverse(TreeNode obj) 
        if (obj != null) 
            for (int i = 0; i < obj.childCount; i++) 
                System.out.println(obj.childrens[i].value);
                traverse(obj.childrens[i]);
            
        
        return;
    

    void printTree(TreeNode obj) 
        System.out.println(obj.value);
        traverse(obj);
    

以及上述实现的客户端类。

public class Client 
    public static void main(String[] args) 
        TreeNode menu = new TreeNode("Menu");
        TreeNode item = menu.addChild("Starter");
            item = item.addChild("Veg");
                item.addChild("Paneer Tikka");
                item.addChild("Malai Paneer Tikka");
            item = item.addChild("Non-veg");
                item.addChild("Chicken Tikka");
                item.addChild("Malai Chicken Tikka");
        item = menu.addChild("Main Course");
            item = item.addChild("Veg");
                item.addChild("Mili Juli Sabzi");
                item.addChild("Aloo Shimla Mirch");
            item = item.addChild("Non-veg");
                item.addChild("Chicken Do Pyaaza");
                item.addChild("Chicken Chettinad");
        item = menu.addChild("Desserts");
                item = item.addChild("Cakes");
                        item.addChild("Black Forest");
                        item.addChild("Black Current");
                item = item.addChild("Ice Creams");
                        item.addChild("chocolate");
                        item.addChild("Vanilla");
        menu.printTree(menu);
    

输出

Menu                                                                     
Starter                                                                 
Veg                                                                
Paneer Tikka                                                        
Malai Paneer Tikka                                                    
Non-veg                                                              
Chicken Tikka                                                      
Malai Chicken Tikka                                                 
Main Course                                                      
Veg                                                             
Mili Juli Sabzi                                                   
Aloo Shimla Mirch                                                  
Non-veg                                                               
Chicken Do Pyaaza                                                   
Chicken Chettinad                                                    
Desserts                                                         
Cakes                                                            
Black Forest                                                     
Black Current                                                   
Ice Creams                                                      
chocolate                                                       
Vanilla           

【讨论】:

【参考方案4】:

由于@Jonathan的answer还是有一些bug,所以我做了一个改进的版本。我为了调试目的重写了toString() 方法,请务必根据您的数据进行相应更改。

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

/**
 * Provides an easy way to create a parent-->child tree while preserving their depth/history.
 * Original Author: Jonathan, https://***.com/a/22419453/14720622
 */
public class TreeNode<T> 
    private final List<TreeNode<T>> children;
    private TreeNode<T> parent;
    private T data;
    private int depth;

    public TreeNode(T data) 
        // a fresh node, without a parent reference
        this.children = new ArrayList<>();
        this.parent = null;
        this.data = data;
        this.depth = 0; // 0 is the base level (only the root should be on there)
    

    public TreeNode(T data, TreeNode<T> parent) 
        // new node with a given parent
        this.children = new ArrayList<>();
        this.data = data;
        this.parent = parent;
        this.depth = (parent.getDepth() + 1);
        parent.addChild(this);
    

    public int getDepth() 
        return this.depth;
    

    public void setDepth(int depth) 
        this.depth = depth;
    

    public List<TreeNode<T>> getChildren() 
        return children;
    

    public void setParent(TreeNode<T> parent) 
        this.setDepth(parent.getDepth() + 1);
        parent.addChild(this);
        this.parent = parent;
    

    public TreeNode<T> getParent() 
        return this.parent;
    

    public void addChild(T data) 
        TreeNode<T> child = new TreeNode<>(data);
        this.children.add(child);
    

    public void addChild(TreeNode<T> child) 
        this.children.add(child);
    

    public T getData() 
        return this.data;
    

    public void setData(T data) 
        this.data = data;
    

    public boolean isRootNode() 
        return (this.parent == null);
    

    public boolean isLeafNode() 
        return (this.children.size() == 0);
    

    public void removeParent() 
        this.parent = null;
    

    @Override
    public String toString() 
        String out = "";
        out += "Node: " + this.getData().toString() + " | Depth: " + this.depth + " | Parent: " + (this.getParent() == null ? "None" : this.parent.getData().toString()) + " | Children: " + (this.getChildren().size() == 0 ? "None" : "");
        for(TreeNode<T> child : this.getChildren()) 
            out += "\n\t" + child.getData().toString() + " | Parent: " + (child.getParent() == null ? "None" : child.getParent().getData());
        
        return out;
    

对于可视化:

import model.TreeNode;

/**
 * Entrypoint
 */
public class Main 
    public static void main(String[] args) 
        TreeNode<String> rootNode = new TreeNode<>("Root");
        TreeNode<String> firstNode = new TreeNode<>("Child 1 (under Root)", rootNode);
        TreeNode<String> secondNode = new TreeNode<>("Child 2 (under Root)", rootNode);
        TreeNode<String> thirdNode = new TreeNode<>("Child 3 (under Child 2)", secondNode);
        TreeNode<String> fourthNode = new TreeNode<>("Child 4 (under Child 3)", thirdNode);
        TreeNode<String> fifthNode = new TreeNode<>("Child 5 (under Root, but with a later call)");
        fifthNode.setParent(rootNode);

        System.out.println(rootNode.toString());
        System.out.println(firstNode.toString());
        System.out.println(secondNode.toString());
        System.out.println(thirdNode.toString());
        System.out.println(fourthNode.toString());
        System.out.println(fifthNode.toString());
        System.out.println("Is rootNode a root node? - " + rootNode.isRootNode());
        System.out.println("Is firstNode a root node? - " + firstNode.isRootNode());
        System.out.println("Is thirdNode a leaf node? - " + thirdNode.isLeafNode());
        System.out.println("Is fifthNode a leaf node? - " + fifthNode.isLeafNode());
    

示例输出:

Node: Root | Depth: 0 | Parent: None | Children: 
    Child 1 (under Root) | Parent: Root
    Child 2 (under Root) | Parent: Root
    Child 5 (under Root, but with a later call) | Parent: Root
Node: Child 1 (under Root) | Depth: 1 | Parent: Root | Children: None
Node: Child 2 (under Root) | Depth: 1 | Parent: Root | Children: 
    Child 3 (under Child 2) | Parent: Child 2 (under Root)
Node: Child 3 (under Child 2) | Depth: 2 | Parent: Child 2 (under Root) | Children: 
    Child 4 (under Child 3) | Parent: Child 3 (under Child 2)
Node: Child 4 (under Child 3) | Depth: 3 | Parent: Child 3 (under Child 2) | Children: None
Node: Child 5 (under Root, but with a later call) | Depth: 1 | Parent: Root | Children: None
Is rootNode a root node? - true
Is firstNode a root node? - false
Is thirdNode a leaf node? - false
Is fifthNode a leaf node? - true

一些附加信息:不要将addChildren()setParent() 一起使用。您最终将获得两个引用,因为 setParent() 已经更新了 children=>parent 关系。

【讨论】:

【参考方案5】:

这棵树不是二叉树,所以你需要一个子元素数组,比如 List。

public Node(Object data, List<Node> children) 
    this.data = data;
    this.children = children;

然后创建实例。

【讨论】:

【参考方案6】:

在接受的答案中

public Node(T data, Node<T> parent) 
    this.data = data;
    this.parent = parent;

应该是

public Node(T data, Node<T> parent) 
    this.data = data;
    this.setParent(parent);

否则父级的子级列表中没有子级

【讨论】:

请添加为评论【参考方案7】:

在answer ,它会创建循环依赖。这可以通过删除子节点中的父节点来避免。 即,

public class MyTreeNode<T>     


    private T data = null;
    private List<MyTreeNode> children = new ArrayList<>();


    public MyTreeNode(T data) 
        this.data = data;

    

    public void addChild(MyTreeNode child) 
        this.children.add(child);
    

    public void addChild(T data) 
        MyTreeNode<T> newChild = new MyTreeNode<>(data);
        children.add(newChild);
    

    public void addChildren(List<MyTreeNode> children) 
        this.children.addAll(children);
    

    public List<MyTreeNode> getChildren() 
        return children;
    

    public T getData() 
        return data;
    

    public void setData(T data) 
        this.data = data;
    



使用上面指定的相同示例,输出将如下所示:

“数据”:“根”,“孩子”:[ “数据”:“孩子1”, “孩子们”: [ “数据”:“孙子 1”, “孩子们”: [] , “数据”:“孙子 2”, “孩子们”: [] ] , “数据”:“孩子2”, “孩子们”: [ “数据”:“孙子 3”, “孩子们”: [] ] , “数据”:“儿童 3”, “孩子们”: [] , “数据”:“Child4”, “孩子们”: [] , “数据”:“儿童 5”, “孩子们”: [] , “数据”:“儿童 6”, “孩子们”: [] ]

【讨论】:

【参考方案8】:

组装树节点的过程类似于组装列表的过程。我们有一个用于初始化实例变量的树节点的构造函数。

public Tree (Object cargo, Tree left, Tree right)  
    this.cargo = cargo; 
    this.left = left; 
    this.right = right; 
 

我们先分配子节点:

Tree left = new Tree (new Integer(2), null, null); 
Tree right = new Tree (new Integer(3), null, null); 

我们可以同时创建父节点并链接到子节点:

Tree tree = new Tree (new Integer(1), left, right); 

【讨论】:

上图不是二叉树。

以上是关于Java中的树实现(根,父母和孩子)的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 PDO 从 PHP 中的单个 select 语句将孩子分配给父母?

从父母发送消息给孩子

使用 AFNetworking 进行证书/SSL 固定 - 如果孩子受信任,如何信任所有父母证书?

来自祖父母的组件,来自父母的数据,在孩子中呈现

反应孩子与父母的沟通问题

从父母向孩子发送信号,反之亦然