Почему С++ 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 может быть вам подходит.
Ответ 6
std:: map основан на красном черном дереве. Вы также можете использовать другие контейнеры, чтобы помочь вам реализовать свои собственные типы деревьев.
Ответ 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 могут использоваться с итераторами. Вы не можете иметь итератор дерево, потому что у вас нет пути "один правильный", который проходит через дерево.