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<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();
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)
应该有equals
和hashCode
方法吗?
这段代码还抛出***Error真的很奇怪【参考方案2】:
Accepted answer 在调用setParent
或addChild
方法时抛出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 语句将孩子分配给父母?