【问题标题】:sort arraylist into tree recursively递归地将arraylist排序到树中
【发布时间】:2014-02-11 21:41:38
【问题描述】:

我有一个Object 的数组列表。但我需要一棵具有以下参数的树:

  1. 某些对象不应该有子对象(非类别对象)
  2. 类别对象应该有子对象(可以是其他类别对象或非类别对象)
  3. 应跳过/删除没有子对象的类别对象

所有对象都有其父对象的变量。但是我没有一个好的算法可以递归地确定每个类别对象有多少个孩子。同一级别的所有孩子都将成为ArrayList<Object>

我目前有一个迭代方法,只能深入一层,无法区分场景2的某些版本。我认为它的代码不相关,因为它不是递归的。

见识见识

【问题讨论】:

  • @AniketThakur 我目前有一个迭代函数,它只能深入一层。它无法区分所有可能的场景

标签: java recursion tree treemap treeset


【解决方案1】:

使用Composite Pattern 为您的所有对象提供一个通用接口。

一个接口或抽象超类,组件,代表所有可能有关系的对象。一个子类 Leaf 没有任何子类。 (可能还有其他非复合类叶子类。)另一个子类,Composite,包含许多对象,通常是任何类型的组件。这允许 Composite 包含其他 Composites 或其他非复合材料,例如 Leaf。

我在这里创建的组件超类是ComponentObject。这里,所有ComponentObjects都有一个父级CategoryObject,下面介绍。

组件对象

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,可以是其他 CategoryObjects 或 NonCategoryObjects。

NonCategoryObject

public class NonCategoryObject extends ComponentObject
{
   public NonCategoryObject(CategoryObject parent)
   {
      super(parent);
   }

   @Override
   public int getNumChildren()
   {
      return 0;
   }
}

类别对象

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 个不同的方法,每个方法都有自己的方法来确定孩子的数量:

   // 2 methods to determine the number of children.
   compositeMethod(foundRoot);
   recursiveMethod(foundRoot);
}

方法

这里是方法。一是“复合”法,它利用复合模式来确定孩子的数量。它只是调用根的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】:

    由于我经常使用您所描述的树结构,因此我前段时间创建了一个虚拟实现。我稍微更改了代码以使其更通用,并根据您的需要对其进行了更新,您可以随意使用它。

    树元素的“id”是通用的,因此您可以使用任何数据作为元素的 id。 在创建一些随机树结构的主要方法中记录了一个示例用法。生成树后,您可以使用 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());
    }
    }
    

    【讨论】:

      【解决方案3】:

      我遇到了类似的问题,但在 Javascript 中。有人在这里用递归算法为我回答了这个问题:

      Convert parent-child array to tree

      它可能并不完美,但希望它能给你一个起点。

      【讨论】:

        猜你喜欢
        • 2017-01-14
        • 2014-08-12
        • 1970-01-01
        • 2010-12-11
        • 2015-08-13
        • 2016-04-19
        • 1970-01-01
        • 2014-09-05
        • 1970-01-01
        相关资源
        最近更新 更多