我有一个Object
的arraylist。但我需要一个具有以下参数的树:
所有对象都有一个父变量的变量。但我没有一个好的算法可以递归地确定每个类别对象有多少个孩子。同一级别的所有孩子都是ArrayList<Object>
我目前有一个只能深入一级的迭代方法,并且无法区分方案2的某些版本。我不认为它的代码是相关的,因为它不是递归的。
洞察力
答案 0 :(得分:3)
由于我经常使用你所描述的树结构,我前段时间创建了一个虚拟实现。我稍微更改了代码以使其更加通用并根据您的需要进行更新,随意在您认为合适的情况下充分利用它。
树元素的“id”是通用的,因此您可以将任何数据用作元素的ID。 在main方法中记录了一个示例用法,它创建了一些随机树结构。生成树后,您可以使用Visitor pattern验证并导航树。
以下是代码:
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
public class TestStructure {
public interface ElementVisitor<E>{
boolean startVisit(Element<E> e);
void endVisit(Element<E> e);
}
public interface Element<E>{
E getId();
public boolean isParent();
Parent<E> getParent();
void visit(ElementVisitor<E> visitor);
}
public interface Parent<E> extends Element<E>{
List<Element<E>> getChildren();
int size();
}
public static abstract class ElementImpl<E> implements Element<E>{
private final E id;
private ParentImpl<E> parent;
public ElementImpl(E id) {
super();
if (id == null) {
throw new IllegalArgumentException("id must not be null");
}
this.id = id;
}
void setParent(ParentImpl<E> parent) {
this.parent = parent;
}
@Override
public final ParentImpl<E> getParent() {
return parent;
}
protected final void addToParent(){
parent.addChild(this);
}
protected final void removeFromParent(){
parent.removeChild(this);
}
public final boolean isTreeRoot(){
return parent == null;
}
@Override
public String toString() {
return getId().toString();
}
public int getTreeLevel() {
int level = 0;
ParentImpl<E> parent = this.parent;
while (parent != null) {
parent = parent.getParent();
level++;
}
return level;
}
public ParentImpl<E> getRoot(){
ElementImpl<E> e = this;
while (e.parent != null) {
e = e.parent;
}
return (ParentImpl<E>) e;
}
public E getId() {
return id;
}
}
public static class ChildImpl<E> extends ElementImpl<E> implements Element<E>{
public ChildImpl(E id) {
super(id);
}
@Override
public void visit(ElementVisitor<E> visitor) {
visitor.startVisit(this);
visitor.endVisit(this);
}
@Override
void setParent(ParentImpl<E> parent) {
super.setParent(parent);
// add childs to parent automatically
addToParent();
}
@Override
public boolean isParent() {
return false;
}
}
public static class ParentImpl<E> extends ElementImpl<E> implements Parent<E>{
private List<ElementImpl<E>> children;
boolean added = false;
public ParentImpl(E id) {
super(id);
}
@SuppressWarnings("unchecked")
@Override
public List<Element<E>> getChildren() {
return children == null ? Collections.EMPTY_LIST : children;
}
public void addChild(ElementImpl<E> child){
if (children == null) {
children = new ArrayList<>();
}
if (getParent() != null && children.size() == 0) {
// only include parents in the tree if they have children
addToParent();
added = true;
}
children.add(child);
}
public boolean removeChild(ElementImpl<E> e) {
if (children != null && children.remove(e)) {
if (children.size() == 0) {
children = null;
removeFromParent();
added = false;
}
}
return false;
}
@Override
void setParent(ParentImpl<E> parent) {
super.setParent(parent);
if (children != null && !added) {
addToParent();
added = true;
}
}
@Override
public int size() {
return children == null ? 0 : children.size();
}
@Override
public void visit(ElementVisitor<E> visitor) {
if (visitor.startVisit(this)) {
for (Element<E> e : getChildren()) {
e.visit(visitor);
}
}
visitor.endVisit(this);
}
@Override
public boolean isParent() {
return true;
}
}
public static void main(String[] args) {
// elements below parentRange are considered parents
int parentRange = 80;
// The number of all elements in the tree
int numberOfElements = 150;
Map<Integer, Integer> relationMap = new HashMap<>(); // map from child id to parent id
Random random = new Random();
// id 0 == root id
// create an initial parent element at the root
relationMap.put(1, 0);
// create the parent elements
for (int id = 2; id < parentRange; id++) {
int parentId = random.nextInt(id-1);
relationMap.put(id, parentId);
}
// create the child elements
for (int id = parentRange; id < numberOfElements; id++) {
int parentId = random.nextInt(parentRange);
relationMap.put(id, parentId);
}
HashMap<Integer, ParentImpl<Integer>> map = new HashMap<>();
final ParentImpl<Integer> treeRoot = new ParentImpl<>(0);
map.put(0, treeRoot);
// create the tree structure
for (Entry<Integer, Integer> entry : relationMap.entrySet()) {
Integer childId = entry.getKey();
Integer parentId = entry.getValue();
ParentImpl<Integer> parentImpl = map.get(parentId);
if (parentImpl == null) {
parentImpl = new ParentImpl<>(parentId);
map.put(parentId, parentImpl);
}
ElementImpl<Integer> child;
if (childId < parentRange) {
// this child is a parent
child = map.get(childId);
if (child == null) {
ParentImpl<Integer> p = new ParentImpl<>(childId);
child = p;
map.put(childId, p);
}
} else{
child = new ChildImpl<>(childId);
}
child.setParent(parentImpl);
}
// count the number of elements in the tree
class Counter implements ElementVisitor<Integer> {
int count = 0;
@Override
public boolean startVisit(Element<Integer> e) {
count++;
return true;
}
@Override
public void endVisit(Element<Integer> e) {}
};
Counter counter = new Counter();
treeRoot.visit(counter);
System.out.println("Number of all elements in the tree: " + counter.count);
// validate the tree structure
ElementVisitor<Integer> treeValidator = new ElementVisitor<Integer>() {
@Override
public boolean startVisit(Element<Integer> e) {
if (!e.getId().equals(0) && e.getParent() == null) {
throw new IllegalStateException("child with no parent...");
}
if (e.isParent()) {
Parent<Integer> parent = (Parent<Integer>)e;
if (parent.size() == 0) {
throw new IllegalStateException("parent with no children...");
}
}
return true;
}
@Override
public void endVisit(Element<Integer> e) {}
};
treeRoot.visit(treeValidator);
final StringBuilder b = new StringBuilder();
// print the tree structure
ElementVisitor<Integer> treePrinter = new ElementVisitor<Integer>() {
int level = 0;
@Override
public boolean startVisit(Element<Integer> e) {
for (int i = 0; i < level; i++) {
b.append(" ");
}
if (e.isParent()) {
b.append("Parent ");
level++;
} else{
b.append("Child ");
}
b.append(e.getId());
b.append('\n');
return true;
}
@Override
public void endVisit(Element<Integer> e) {
if (e.isParent()) {
level--;
}
}
};
treeRoot.visit(treePrinter);
System.out.println("The generated tree structure: ");
System.out.println(b.toString());
}
}
答案 1 :(得分:3)
使用Composite Pattern为所有对象提供通用界面。
接口或抽象超类 Component 表示可能具有关系的所有对象。一个子类 Leaf 没有任何子节点。 (可能还有其他非复合的Leaf类子类。)另一个子类 Composite 包含许多对象,通常是任何类型的Component。这允许Composite包含其他复合材料,或其他非复合材料,如Leaf。
我在这里创建的Component超类是ComponentObject
。在此,所有ComponentObjects
都有一个父CategoryObject
,在下面介绍。
<强> ComponentObject 强>
public abstract class ComponentObject
{
private CategoryObject myParent;
protected ComponentObject(CategoryObject parent)
{
myParent = parent;
}
public CategoryObject getParent()
{
return myParent;
}
public abstract int getNumChildren();
}
它有两个具体的子类NonCategoryObject
,Leaf不能包含子节点,CategoryObject
,Composite,它封装了一个子对象列表,可以是其他CategoryObject
个或NonCategoryObject
s。
<强> NonCategoryObject 强>
public class NonCategoryObject extends ComponentObject
{
public NonCategoryObject(CategoryObject parent)
{
super(parent);
}
@Override
public int getNumChildren()
{
return 0;
}
}
<强> CategoryObject 强>
public class CategoryObject extends ComponentObject
{
private ArrayList<ComponentObject> myChildren;
public CategoryObject(CategoryObject parent)
{
super(parent);
myChildren = new ArrayList<ComponentObject>();
}
public void addChild(ComponentObject child)
{
myChildren.add(child);
}
public ComponentObject getChild(int index)
{
return myChildren.get(index);
}
public int getNumDirectChildren()
{
return myChildren.size();
}
@Override
public int getNumChildren()
{
int numChildren = 0;
for (ComponentObject child : myChildren)
{
// One for the child itself, plus add any of the child's children.
numChildren += 1 + child.getNumChildren();
}
return numChildren;
}
}
“getNumChildren”方法对于通过复合模式查找子项数非常重要。
设置数据
你有ArrayList
个组件,每个组件都知道他们的父母,但不知道他们的孩子是谁:
public static void main(String[] args)
{
// Create a tree structure, parent information only.
CategoryObject root = new CategoryObject(null);
CategoryObject categoryOne = new CategoryObject(root);
CategoryObject categoryTwo = new CategoryObject(root);
CategoryObject categoryThree = new CategoryObject(root);
CategoryObject categoryOneOne = new CategoryObject(categoryOne);
CategoryObject categoryOneTwo = new CategoryObject(categoryOne);
CategoryObject categoryOneThree = new CategoryObject(categoryOne);
NonCategoryObject leafOneFour = new NonCategoryObject(categoryOne);
NonCategoryObject leafOneOneOne = new NonCategoryObject(categoryOneOne);
NonCategoryObject leafOneOneTwo = new NonCategoryObject(categoryOneTwo);
NonCategoryObject leafOneTwoOne = new NonCategoryObject(categoryOneTwo);
NonCategoryObject leafOneThreeOne = new NonCategoryObject(categoryOneThree);
NonCategoryObject leafTwoOne = new NonCategoryObject(categoryTwo);
NonCategoryObject leafThreeOne = new NonCategoryObject(categoryThree);
// We're given the ArrayList
ArrayList<ComponentObject> components = new ArrayList<ComponentObject>();
// The order doesn't matter.
components.addAll(Arrays.asList(leafOneFour, leafOneOneTwo, leafOneThreeOne,
categoryOne, categoryOneOne, categoryOneThree, root, leafThreeOne,
leafOneOneOne, categoryThree, leafOneTwoOne, leafTwoOne,
categoryTwo, categoryOneTwo));
通过循环遍历列表来确定子信息。我们也会找到根(假设只有一个无父组件)。
ComponentObject foundRoot = null;
for (ComponentObject c : components)
{
CategoryObject parent = c.getParent();
if (parent == null)
{
foundRoot = c;
}
else
{
parent.addChild(c);
}
}
现在所有的父母都知道他们的孩子是谁。接下来,我们将调用两种不同的方法,每种方法都有自己确定子项数的方法:
// 2 methods to determine the number of children.
compositeMethod(foundRoot);
recursiveMethod(foundRoot);
}
方法
以下是方法。首先,“复合”方法利用复合模式来确定子项数。它只是调用root的getNumChildren()
方法。
private static void compositeMethod(ComponentObject root)
{
int numChildren = root.getNumChildren();
System.out.println("Composite method: " + numChildren);
}
输出:
Composite method: 13
复合方法不是很递归,因为即使它调用自身,它调用自身的对象也是不同的。它把自己称为对象的孩子。
您感兴趣的递归方法在层次结构中的每个级别调用自身:
private static void recursiveMethod(ComponentObject root)
{
int numChildren = findNumChildren(root);
System.out.println("Recursive method: " + numChildren);
}
// The actual recursive method.
private static int findNumChildren(ComponentObject root)
{
if (root instanceof CategoryObject)
{
CategoryObject parent = (CategoryObject) root;
int numChildren = 0;
for (int i = 0; i < parent.getNumDirectChildren(); i++)
{
ComponentObject child = parent.getChild(i);
// One for the child itself, plus its children.
numChildren += 1 + findNumChildren(child);
}
return numChildren;
}
// Base case: NonCategoryObjects have no children.
return 0;
}
输出:
Recursive method: 13
答案 2 :(得分:1)