Почему С++ STL не предоставляет никаких "древовидных" контейнеров?

Почему С++ STL не предоставляет никаких "древовидных" контейнеров и что лучше всего использовать вместо этого?

Я хочу сохранить иерархию объектов как дерево, а не использовать дерево в качестве повышения производительности...

Ответ 1

Есть две причины, по которым вы можете использовать дерево:

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

Или вам нужен контейнер с древовидными характеристиками доступа. Для этого мы имеем

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

Смотрите также этот вопрос: Реализация дерева C

Ответ 2

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

Некоторые вопросы для рассмотрения:
 - Является ли число детей для node фиксированным или переменным?
 - Сколько накладных расходов на node? - т.е. вам нужны родительские указатели, указатели для сестер и т.д.
 - Какие алгоритмы предоставить? - разные итераторы, алгоритмы поиска и т.д.

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

Вот некоторые другие общие реализации дерева:
 - дерево Kasper Peeters.hh
 - Adobe forest
 - core:: tree

Ответ 3

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

Ответ 4

"Я хочу сохранить иерархию объектов как дерево"

С++ 11 пришел и ушел, и они все еще не видели необходимости предоставлять std::tree, хотя идея действительно появилась (см. здесь). Возможно, причина, по которой они не добавили этого, заключается в том, что тривиально легко создавать свои собственные поверх существующих контейнеров. Например...

template< typename T >
struct tree_node
   {
   T t;
   std::vector<tree_node> children;
   };

Простой обход будет использовать рекурсию...

template< typename T >
void tree_node<T>::walk_depth_first() const
   {
   cout<<t;
   for ( auto & n: children ) n.walk_depth_first();
   }

Если вы хотите сохранить иерархию и хотите, чтобы она работала с алгоритмами STL, тогда ситуация может осложниться. Вы можете создавать собственные итераторы и добиваться некоторой совместимости, однако многие алгоритмы просто не имеют никакого смысла для иерархии (что-то, что изменяет порядок диапазона, например). Даже определение диапазона внутри иерархии может быть беспорядочным бизнесом.

Ответ 5

Если вы ищете реализацию RB-дерева, то stl_tree.h может быть вам подходит.

Ответ 7

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

В общем, если есть базовая библиотека, которую вы хотите, что не в stl, исправление должно выглядеть BOOST.

В противном случае существует bunch библиотеки out там, в зависимости от потребностей вашего дерева.

Ответ 8

Все контейнеры STL внешне представлены как "последовательности" с одним итерационным механизмом. Деревья не следуют этой идиоме.

Ответ 9

Поскольку STL не является библиотекой "все". Он содержит, по существу, минимальные структуры, необходимые для создания вещей.

Ответ 10

Этот выглядит многообещающим и, похоже, является тем, что вы ищете: http://tree.phi-sci.com/

Ответ 11

ИМО, упущение. Но я думаю, что есть хорошая причина не включать структуру дерева в STL. Существует много логики в сохранении дерева, которое лучше всего записывается как функции-члены в базовый объект TreeNode. Когда TreeNode завершается в заголовке STL, он просто становится беспорядочным.

Например:

template <typename T>
struct TreeNode
{
  T* DATA ; // data of type T to be stored at this TreeNode

  vector< TreeNode<T>* > children ;

  // insertion logic for if an insert is asked of me.
  // may append to children, or may pass off to one of the child nodes
  void insert( T* newData ) ;

} ;

template <typename T>
struct Tree
{
  TreeNode<T>* root;

  // TREE LEVEL functions
  void clear() { delete root ; root=0; }

  void insert( T* data ) { if(root)root->insert(data); } 
} ;

Ответ 12

Я думаю, есть несколько причин, почему нет stl-деревьев. В основном деревья - это форма рекурсивной структуры данных, которая, подобно контейнеру (списку, вектору, множеству), имеет очень отличную тонкую структуру, что делает правильный выбор сложным. Их также очень легко построить в базовой форме с помощью STL.

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

template<class A>
struct unordered_tree : std::set<unordered_tree>, A
{};

template<class A>
struct b_tree : std::vector<b_tree>, A
{};

template<class A>
struct planar_tree : std::list<planar_tree>, A
{};

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

Деревья играют важную роль во многих математических структурах (см. классические работы Бутчера, Гроссмана и Ларсена, а также статьи Конна и Кримера для примеров того, как они могут быть объединены и как они используются для перечисления). Неправильно думать, что их роль - просто облегчить некоторые другие операции. Скорее они облегчают эти задачи из-за их фундаментальной роли в качестве структуры данных.

Однако в дополнение к деревьям есть также "со-деревья"; деревья, прежде всего, обладают тем свойством, что если вы удаляете корень, вы удаляете все.

Рассмотрим итераторы на дереве, вероятно, они будут реализованы как простой стек итераторов, к node и к его родительскому объекту... до корня.

template<class TREE>
struct node_iterator : std::stack<TREE::iterator>{
operator*() {return *back();}
...};

Однако у вас может быть столько, сколько вам нравится; в совокупности они образуют "дерево", но там, где все стрелки текут в направлении к корню, это совместное дерево может повторяться, хотя итераторы к тривиальному итератору и корню; однако он не может быть перемещен поперек или вниз (другие итераторы ему неизвестны), и ансамбль итераторов не может быть удален, за исключением отслеживания всех экземпляров.

Деревья невероятно полезны, у них много структуры, это создает серьезную проблему для получения окончательно правильного подхода. На мой взгляд, именно поэтому они не реализованы в STL. Более того, в прошлом я видел, как люди стали религиозными и находили идею типа контейнера, содержащего экземпляры своего типа, сложного - но им приходится сталкиваться с этим - это то, что представляет собой тип дерева - это node содержащий возможно пустую коллекцию (меньших) деревьев. Текущий язык позволяет без проблем предоставить конструктору по умолчанию для container<B> не выделять пространство в куче (или где-либо еще) для B и т.д.

Мне было бы приятно, если бы это было в хорошей форме, чтобы найти свой путь в стандарт.

Ответ 13

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