Древовидная структура Java с несколькими дочерними элементами (отсортированными) на каждом уровне - PullRequest
9 голосов
/ 20 января 2011

Я работаю с плоским списком объектов, которые, тем не менее, связаны друг с другом в родительско-дочерних отношениях.Объект может иметь любое количество детей или вообще не иметь их.Мне нужно отобразить эти объекты в виде дерева, показывая эти отношения.Каждый уровень дерева должен быть отсортирован (объекты совместимы с Collections.sort()).

Вопрос состоит из двух частей:

  1. Имеет ли Java хорошую готовую структуру данных для хранения такого дерева, или мне нужнонаписать с нуля?(не большая задача, но нет смысла заново изобретать колесо) Я знаю о DefaultTreeModel в Swing ... но это приложение работает на стороне сервера, и использование пакета Swing будет неодобрительно в обзоре кода.

  2. Каков наилучший шаблон для загрузки плоского списка в такую ​​структуру данных?Моя первая мысль состоит в том, чтобы идентифицировать объекты корневого уровня, а затем использовать рекурсивный метод, чтобы пройти через своих детей, внуков и т. Д. Однако, для требования сортировки пиров на каждом уровне в дереве ... Яне уверен, имеет ли смысл беспокоиться об этом, когда я строю дерево, или беспокоиться об этом позже, когда я анализирую дерево для отображения.

Ответы [ 4 ]

9 голосов
/ 20 января 2011

Вот быстрая и грязная реализация Tree, которая использует TreeSets на всех уровнях (вы можете предоставить компаратор, или будет использоваться естественный порядок):

public class Tree<T> {

    private final Node<T> rootElement;

    public void visitNodes(final NodeVisitor<T> visitor){
        doVisit(rootElement, visitor);
    }

    private static <T> boolean doVisit(final Node<T> node,
        final NodeVisitor<T> visitor){
        boolean result = visitor.visit(node);
        if(result){
            for(final Node<T> subNode : node.children){
                if(!doVisit(subNode, visitor)){
                    result = false;
                    break;
                }
            }
        }
        return result;
    }

    public interface NodeVisitor<T> {

        boolean visit(Node<T> node);
    }

    public Node<T> getRootElement(){
        return rootElement;
    }

    private static final class NodeComparator<T> implements Comparator<Node<T>>{

        private final Comparator<T> wrapped;

        @Override
        public int compare(final Node<T> o1, final Node<T> o2){
            return wrapped.compare(o1.value, o2.value);
        }

        public NodeComparator(final Comparator<T> wrappedComparator){
            this.wrapped = wrappedComparator;
        }

    }

    public static class Node<T> {

        private final SortedSet<Node<T>> children;

        private final Node<T> parent;

        private T value;

        private final Comparator<?> comparator;

        @SuppressWarnings("unchecked")
        Node(final T value, final Node<T> parent, final Comparator<?> comparator){
            this.value = value;
            this.parent = parent;
            this.comparator = comparator;
            children =
                new TreeSet<Node<T>>(new NodeComparator<T>((Comparator<T>) comparator));
        }

        public List<Node<T>> getChildren(){
            return new ArrayList<Node<T>>(children);
        }

        public Node<T> getParent(){
            return parent;
        }

        public T getValue(){
            return value;
        }

        public void setValue(final T value){
            this.value = value;
        }

        public Node<T> addChild(final T value){
            final Node<T> node = new Node<T>(value, this, comparator);
            return children.add(node) ? node : null;
        }

    }

    @SuppressWarnings("rawtypes")
    private static final Comparator NATURAL_ORDER = new Comparator(){

        @SuppressWarnings("unchecked")
        @Override
        public int compare(final Object o1, final Object o2){
            return ((Comparable) o1).compareTo(o2);
        }
    };

    private final Comparator<?> comparator;

    public Tree(){
        this(null, null);
    }

    public Tree(final Comparator<? super T> comparator){
        this(comparator, null);
    }

    public Tree(final Comparator<? super T> comparator, final T rootValue){
        this.comparator = comparator == null ? NATURAL_ORDER : comparator;
        this.rootElement = new Node<T>(rootValue, null, this.comparator);
    }

    public Tree(final T rootValue){
        this(null, rootValue);
    }

}

Вот пример кода против него:

final Tree<Integer> tree = new Tree<Integer>();
final Node<Integer> rootNode = tree.getRootElement();
rootNode.setValue(1);
final Node<Integer> childNode = rootNode.addChild(2);
final Node<Integer> newChildNode = rootNode.addChild(3);
newChildNode.addChild(4);
tree.visitNodes(new NodeVisitor<Integer>(){

    @Override
    public boolean visit(final Node<Integer> node){
        final StringBuilder sb = new StringBuilder();
        Node<Integer> curr = node;
        do{
            if(sb.length() > 0){
                sb.insert(0, " > ");
            }
            sb.insert(0, String.valueOf(curr.getValue()));
            curr = curr.getParent();
        } while(curr != null);
        System.out.println(sb);
        return true;
    }
});

Выход:

1
1> 2
1> 3
1> 3> 4

4 голосов
/ 20 января 2011

Каков наилучший шаблон для загрузки плоского списка в такую ​​структуру данных?Моя первая мысль - идентифицировать объекты корневого уровня, а затем использовать рекурсивный метод, чтобы пройти через своих детей, внуков и т. Д.

Если я правильно понимаю, у вас есть только плоский список,без каких-либо конкретных связей между его элементами, и вы можете каким-то образом определить, является ли определенный элемент дочерним по отношению к другому.

В этом случае вы можете

  1. отсортировать список
  2. (определить корневой узел, если он еще не известен)
  3. поместить корень в очередь
  4. взять первый узел из очереди
  5. , начиная спервый элемент списка, проверьте каждый элемент, является ли он дочерним по отношению к текущему узлу;если это так, добавьте его к текущему уровню дерева и поместите в очередь
  6. повторите с шага 4.

Если обнаружение родительско-дочерних отношений стоит дорого, вы могли бы улучшитьпроизводительность путем сохранения флага для / обнуления каждого узла, местоположение которого в дереве уже идентифицировано, так что вы можете перепрыгивать через них при обходе списка.В качестве альтернативы, вы можете скопировать весь отсортированный список в связанный список, чтобы тривиально удалить из него обработанные элементы.

1 голос
/ 20 января 2011

В Java нет древовидных структур, но есть отсортированные: TreeSet и TreeMap. См. Некоторые советы структура данных Java для имитации дерева данных

0 голосов
/ 20 января 2011

Подход, который вы придумали, - это то, что я бы сделал.

Способ построения дерева действительно зависит от того, какая информация у вас есть в первоначальном Списке.

  • Если каждый узел содержит ссылку на своего родителя и коллекцию его дочерних элементов, вам не нужно создавать ничего, кроме корневого набора.

  • Если каждый узел имеет ссылку только на своего родителя, вам нужно построить дерево; но вы можете сделать это за один проход по данным, используя HashMap для сопоставления каждого узла списку (который вы создаете) его дочерних элементов.

  • Если узлы даже не содержат ссылки на своих родителей, вам придется делать то, что предлагает Петер.

В любом случае, я бы не стал сначала сортировать весь Список. Сортировка большого списка будет медленнее, чем сортировка множества маленьких с одинаковой общей длиной. (Это следует из сортировки O (n log n).)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...