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

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

【问题讨论】:

    标签: java tree structure


    【解决方案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.StackOverflowError 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方法吗?
    • 这段代码还抛出StackOverflowError真的很奇怪
    【解决方案2】:

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

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

    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】:

      由于@Jonathananswer还是有一些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://stackoverflow.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); 
            

            【讨论】:

            • 上图不是二叉树。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-03-02
            • 1970-01-01
            • 2011-02-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多