Почти у каждого проекта, который имеет какую-либо редактируемую модель или документ, будет иерархическая структура. Это может пригодиться для реализации «иерархического узла» в качестве базового класса для различных объектов. Часто связанный список (дочерний брат, 2-я модель) является естественным способом роста многих библиотек классов, однако дочерние объекты могут быть разных типов, и, вероятно, « объектная модель » - это не то, что мы рассматриваем, когда говорим о деревьях в целом.
Моя любимая реализация дерева (узла) вашей первой модели - это однострочная (в C #):
public class node : List<node> { /* props go here */ }
Наследовать от универсального списка вашего собственного типа (или наследовать от любого другого универсального набора ваших собственных типов). Ходьба возможна в одном направлении: сформируйте корень вниз (предметы не знают своих родителей).
Только родительское дерево
Другая модель, которую вы не упомянули, это та, где каждый ребенок имеет ссылку на своего родителя:
null
|
+---------+---------------------------------+
| parent |
| root |
+-------------------------------------------+
| | |
+---------+------+ +-------+--------+ +--+-------------+
| parent | | parent | | parent |
| node 1 | | node 2 | | node 3 |
+----------------+ +----------------+ +----------------+
Обходить это дерево можно только наоборот, обычно все эти узлы будут храниться в коллекции (массив, хеш-таблица, словарь и т. Д.), И узел будет найден путем поиска в коллекции по критериям, отличным от иерархической позиции в дерево, которое обычно не будет иметь первостепенного значения.
Эти только родительские деревья обычно видны в приложениях базы данных. Найти дочерние элементы узла с помощью операторов SELECT * WHERE ParentId = x довольно просто. Однако мы редко находим их преобразованными в объекты класса дерева-узла как таковые. В приложениях Statefull (для настольных компьютеров) они могут быть включены в существующие элементы управления узлов деревьев. В приложениях без сохранения состояния (веб) даже это может быть маловероятным. Я видел, как инструменты генерации классов ORM-карт генерируют ошибки переполнения стека при генерации классов для таблиц, которые имеют отношение к себе (смех), так что, возможно, эти деревья не так уж и распространены.
двунаправленные судоходные деревья
Однако в большинстве практических случаев удобно иметь лучшее из обоих миров. Узлы, которые имеют список дочерних элементов и, кроме того, знают своих родителей: двунаправленные навигационные деревья.
null
|
+--------------------+--------------------+
| parent |
| root |
| child1 child2 child3 |
+--+------------------+----------------+--+
| | |
+---------+-----+ +-------+-------+ +---+-----------+
| parent | | parent | | parent |
| node1 | | node2 | | node3 |
| child1 child2 | | child1 child2 | | child1 child2 |
+--+---------+--+ +--+---------+--+ +--+---------+--+
| | | | | |
Это включает в себя еще много аспектов для рассмотрения:
- Где реализовать связывание и открепление родительского?
- позвольте бизнес-логике позаботиться и оставить аспект вне узла (они забудут!)
- узлы имеют методы для создания дочерних элементов (не позволяет переупорядочивать) (выбор Microsoft в их DOM-реализации System.Xml.XmlDocument, что почти свело меня с ума, когда я впервые столкнулся с этим)
- Узлы принимают родительский элемент в своем конструкторе (не позволяет переупорядочивать)
- во всех методах add (), insert () и remove () и их перегрузках узлов (обычно мой выбор)
- Настойчивость
- Как пройтись по дереву при сохранении (не указывать, например, родительские ссылки)
- Как восстановить двустороннюю связь после десериализации (снова установив всех родителей как действие после десериализации)
- Уведомления
- Статические механизмы (флаг IsDirty), рекурсивно обрабатывать в свойствах?
- События, всплывающие через родителей, вниз через детей, или обоими способами (например, рассмотрите насос сообщений Windows).
Теперь, чтобы ответить на вопрос , двунаправленные навигационные деревья имеют тенденцию (пока что в моей карьере и области) наиболее широко используемые. Примерами являются реализация Microsoft System.Windows.Forms.Control или System.Web.UI.Control в среде .Net, но также каждая реализация DOM (объектная модель документа) будет иметь узлы, которые знают своего родителя, а также перечисление их детей. Причина: простота использования, а не простота реализации. Кроме того, это, как правило, базовые классы для более специфических классов (XmlNode может являться базой для классов Tag, Attribute и Text), и эти базовые классы являются естественными местами для размещения общих сериализаций и архитектур обработки событий.
Дерево лежит в основе многих архитектур, и способность свободно перемещаться означает способность быстрее реализовывать решения.