5

Как реализовать структуру данных "дерево" на Java?

14

Есть ли стандартный класс библиотеки Java для представления дерева?

Мне необходимо создать структуру данных для дерева с следующими характеристиками:

  • Поддерево в любом узле может иметь произвольное количество дочерних узлов.
  • Каждый узел (кроме корневого) и его дочерние узлы будут иметь строковое значение.
  • Мне нужно получить всех детей (некую структуру данных, например, список или массив строк) данного узла, а также его строковое значение (т.е. метод, который будет принимать узел в качестве входного параметра и возвращать все строковые значения дочерних узлов в качестве результата).

Существует ли уже готовая структура для этого или мне нужно реализовать свою собственную? Если да, буду признателен за предложения по реализации.

5 ответ(ов)

3

Вот пример базовой структуры дерева на Java:

public class Tree<T> {
    private Node<T> root;

    public Tree(T rootData) {
        root = new Node<T>();
        root.data = rootData;
        root.children = new ArrayList<Node<T>>();
    }

    public static class Node<T> {
        private T data;
        private Node<T> parent;
        private List<Node<T>> children;
    }
}

Это базовая структура дерева, которую можно использовать для работы со строками или любыми другими объектами. Реализовать простые деревья для ваших нужд довольно просто.

Все, что вам нужно добавить, — это методы для добавления, удаления, обхода и конструкторы. Node является основным строительным блоком дерева.

1

Ваш код представляет собой реализацию простой структуры дерева в Java с использованием обобщений. Дерево состоит из узлов, которые содержат данные, ссылку на родительский узел и список дочерних узлов.

Вот основные моменты вашей реализации:

  1. Класс TreeNode:

    • Параметризован типом <T>, что позволяет использовать любой тип данных для хранения в узлах дерева.
    • В конструкторе инициализируется поле data, а также создается пустой список children для дочерних узлов.
  2. Метод addChild(T child):

    • Создает новый узел с переданными данными, устанавливает текущий узел в качестве родителя и добавляет его в список дочерних узлов.
    • Возвращает созданный дочерний узел для возможного дальнейшего использования.
  3. Пример использования:

    • Создается корневой узел ("root") и добавляются дочерние узлы ("node0", "node1", "node2").
    • Внутри узла "node2" добавляются еще узлы, включая узел с null в данных.
  4. Бонус: Вы упомянули, что у вас есть полная реализация дерева, включая итератор и методы поиска, доступные на GitHub по ссылке.

Если у вас есть дополнительные вопросы относительно реализации или возможно улучшения, дайте знать!

0

Ваш код реализует структуру данных в виде дерева на языке Java. Давайте разберем ключевые моменты, которые могут быть полезны для понимания его работы:

  1. Класс Tree: Это обобщенный класс, который принимает параметр типа <T>, представляющий тип объекта в дереве.

  2. Поля:

    • head: главная (корневая) нода текущего дерева.
    • leafs: список дочерних деревьев (листьев).
    • parent: ссылка на родительское дерево.
    • locate: хэш-карта, позволяющая быстро находить дерево по его корневому элементу.
  3. Конструктор: Конструктор класса принимает значение типа T и добавляет это значение в хэш-карту locate.

  4. Методы:

    • addLeaf(T root, T leaf): добавляет лист к дереву. Если родитель не найден, он создает новое поддерево.
    • addLeaf(T leaf): создает новый лист и добавляет его в список leafs.
    • setAsParent(T parentRoot): назначает текущий экземпляр дерева как дочерний для нового дерева, которое создается с указанным корнем.
    • getHead(), getTree(T element), getParent(): стандартные методы для получения информации о дереве.
    • getSuccessors(T root): возвращает коллекцию всех дочерних элементов указанного узла.
    • getSubTrees(): возвращает коллекцию поддеревьев.
    • toString() и printTree(int increment): методы для текстового представления дерева с учетом отступов.
  5. Статический метод: getSuccessors(T of, Collection<Tree<T>> in) ищет заданный элемент в коллекции деревьев и возвращает его потомков.

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

Если у вас есть конкретные вопросы о данном коде или его реализации, не стесняйтесь их задавать!

0

Ваш код представляет собой реализацию класса Tree, который содержит дерево, где каждая вершина может иметь несколько дочерних элементов (листьев) и ссылку на родительский элемент. Вы правы, что для полного функционала стоит добавить методы, позволяющие добавлять и удалять дочерние элементы. Вот пример того, как это можно сделать:

public class Tree {
    private List<Tree> leaves = new LinkedList<>();
    private Tree parent = null;
    private String data;

    public Tree(String data, Tree parent) {
        this.data = data;
        this.parent = parent;
    }

    // Метод для добавления дочернего элемента
    public void addChild(Tree child) {
        if (child != null) {
            leaves.add(child);
        }
    }

    // Метод для удаления дочернего элемента
    public boolean removeChild(Tree child) {
        return leaves.remove(child);
    }

    // Геттер для данных
    public String getData() {
        return data;
    }

    // Геттер для родительского элемента
    public Tree getParent() {
        return parent;
    }

    // Метод для получения дочерних элементов
    public List<Tree> getLeaves() {
        return new LinkedList<>(leaves); // Возвращаем копию списка
    }
}

В этом обновленном классе:

  1. addChild(Tree child): Метод добавляет дочерний элемент к текущему узлу.
  2. removeChild(Tree child): Метод удаляет дочерний элемент, если он существует.
  3. Геттеры: Также добавлены геттеры для получения данных узла и его родителя, а также метод для получения списка дочерних элементов.

Эти методы позволят вам управлять иерархией дерева более удобно.

0

Чтобы начать, вам следует сначала определить, что такое дерево (в данной области), и это лучше всего сделать, определив интерфейс. Не все структуры деревьев модифицируемы, возможность добавлять и удалять узлы должна быть опциональной функцией, поэтому мы создадим дополнительный интерфейс для этого.

Нет необходимости создавать объекты узлов, которые хранят значения, на самом деле, я рассматриваю это как серьезный недостаток проектирования и лишнюю нагрузку в большинстве реализаций деревьев. Если вы посмотрите на Swing, вы увидите, что TreeModel свободен от классов узлов (только DefaultTreeModel использует TreeNode), поскольку они на самом деле не нужны.

public interface Tree <N extends Serializable> extends Serializable {
    List<N> getRoots ();
    N getParent (N node);
    List<N> getChildren (N node);
}

Для модифицируемой структуры дерева (которая позволяет добавлять и удалять узлы):

public interface MutableTree <N extends Serializable> extends Tree<N> {
    boolean add (N parent, N node);
    boolean remove (N node, boolean cascade);
}

Учитывая эти интерфейсы, код, который использует деревья, не должен сильно заботиться о том, как дерево реализовано. Это позволяет вам использовать обобщенные реализации, а также специализированные, при которых реализация дерева делегирует функции другой API.

Пример: структура дерева файлов

public class FileTree implements Tree<File> {
    @Override
    public List<File> getRoots() {
        return Arrays.stream(File.listRoots()).collect(Collectors.toList());
    }

    @Override
    public File getParent(File node) {
        return node.getParentFile();
    }

    @Override
    public List<File> getChildren(File node) {
        if (node.isDirectory()) {
            File[] children = node.listFiles();
            if (children != null) {
                return Arrays.stream(children).collect(Collectors.toList());
            }
        }
        return Collections.emptyList();
    }
}

Пример: обобщенная структура дерева (основанная на отношениях родитель/дитя):

public class MappedTreeStructure<N extends Serializable> implements MutableTree<N> {
    public static void main(String[] args) {
        MutableTree<String> tree = new MappedTreeStructure<>();
        tree.add("A", "B");
        tree.add("A", "C");
        tree.add("C", "D");
        tree.add("E", "A");
        System.out.println(tree);
    }

    private final Map<N, N> nodeParent = new HashMap<>();
    private final LinkedHashSet<N> nodeList = new LinkedHashSet<>();

    private void checkNotNull(N node, String parameterName) {
        if (node == null)
            throw new IllegalArgumentException(parameterName + " must not be null");
    }

    @Override
    public boolean add(N parent, N node) {
        checkNotNull(parent, "parent");
        checkNotNull(node, "node");

        // проверка на циклы
        N current = parent;
        do {
            if (node.equals(current)) {
                throw new IllegalArgumentException(" node must not be the same or an ancestor of the parent");
            }
        } while ((current = getParent(current)) != null);

        boolean added = nodeList.add(node);
        nodeList.add(parent);
        nodeParent.put(node, parent);
        return added;
    }

    @Override
    public boolean remove(N node, boolean cascade) {
        checkNotNull(node, "node");

        if (!nodeList.contains(node)) {
            return false;
        }
        if (cascade) {
            for (N child : getChildren(node)) {
                remove(child, true);
            }
        } else {
            for (N child : getChildren(node)) {
                nodeParent.remove(child);
            }
        }
        nodeList.remove(node);
        return true;
    }

    @Override
    public List<N> getRoots() {
        return getChildren(null);
    }

    @Override
    public N getParent(N node) {
        checkNotNull(node, "node");
        return nodeParent.get(node);
    }

    @Override
    public List<N> getChildren(N node) {
        List<N> children = new LinkedList<>();
        for (N n : nodeList) {
            N parent = nodeParent.get(n);
            if (node == null && parent == null) {
                children.add(n);
            } else if (node != null && parent != null && parent.equals(node)) {
                children.add(n);
            }
        }
        return children;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        dumpNodeStructure(builder, null, "- ");
        return builder.toString();
    }

    private void dumpNodeStructure(StringBuilder builder, N node, String prefix) {
        if (node != null) {
            builder.append(prefix);
            builder.append(node.toString());
            builder.append('\n');
            prefix = "  " + prefix;
        }
        for (N child : getChildren(node)) {
            dumpNodeStructure(builder, child, prefix);
        }
    }
}

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь