Почему С++ STL не предоставляет никаких "древовидных" контейнеров и что лучше всего использовать вместо этого?
Я хочу сохранить иерархию объектов как дерево, а не использовать дерево в качестве повышения производительности...
Почему С++ STL не предоставляет никаких "древовидных" контейнеров и что лучше всего использовать вместо этого?
Я хочу сохранить иерархию объектов как дерево, а не использовать дерево в качестве повышения производительности...
Есть две причины, по которым вы можете использовать дерево:
Вы хотите отразить проблему, используя древовидную структуру:
Для этого у нас есть библиотека графов повышения
Или вам нужен контейнер с древовидными характеристиками доступа. Для этого мы имеем
std::map
(и std::multimap
)std::set
(и std::multiset
)В основном характеристики этих двух контейнеров таковы, что они практически должны быть реализованы с использованием деревьев (хотя на самом деле это не является обязательным требованием).
Смотрите также этот вопрос: Реализация дерева C
Вероятно, по той же причине, что в boost нет контейнера с деревьями. Существует множество способов реализовать такой контейнер, и нет хорошего способа удовлетворить всех, кто его использует.
Некоторые вопросы для рассмотрения:
- Является ли число детей для node фиксированным или переменным?
- Сколько накладных расходов на node? - т.е. вам нужны родительские указатели, указатели для сестер и т.д.
- Какие алгоритмы предоставить? - разные итераторы, алгоритмы поиска и т.д.
В конце концов проблема заканчивается тем, что контейнер дерева, который был бы достаточно полезен для всех, был бы слишком тяжелым, чтобы удовлетворить большинство людей, использующих его. Если вы ищете что-то мощное, Boost Graph Library по сути является надмножеством того, что можно использовать для библиотеки дерева.
Вот некоторые другие общие реализации дерева:
- дерево Kasper Peeters.hh
- Adobe forest
- core:: tree
Философия STL заключается в том, что вы выбираете контейнер на основе гарантий и не основываетесь на том, как реализуется контейнер. Например, ваш выбор контейнера может основываться на необходимости быстрого поиска. Для всего, что вам нужно, контейнер может быть реализован как однонаправленный список - если поиск выполняется очень быстро, вы будете счастливы. Это потому, что вы все равно не трогаете внутренности, вы используете итераторы или функции-члены для доступа. Ваш код не связан с тем, как реализуется контейнер, но насколько он быстрым, или имеет фиксированное и определенное упорядочение, или он эффективен в пространстве и т.д.
"Я хочу сохранить иерархию объектов как дерево"
С++ 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, тогда ситуация может осложниться. Вы можете создавать собственные итераторы и добиваться некоторой совместимости, однако многие алгоритмы просто не имеют никакого смысла для иерархии (что-то, что изменяет порядок диапазона, например). Даже определение диапазона внутри иерархии может быть беспорядочным бизнесом.
Если вы ищете реализацию RB-дерева, то stl_tree.h может быть вам подходит.
std:: map основан на красном черном дереве. Вы также можете использовать другие контейнеры, чтобы помочь вам реализовать свои собственные типы деревьев.
В некотором роде std:: map - это дерево (оно должно иметь те же характеристики производительности, что и сбалансированное двоичное дерево), но не предоставляет других функций дерева. Вероятная аргументация в том, что не включая реальную структуру данных дерева, вероятно, была всего лишь вопросом не включать все в stl. Stl можно рассматривать как структуру для использования в реализации ваших собственных алгоритмов и структур данных.
В общем, если есть базовая библиотека, которую вы хотите, что не в stl, исправление должно выглядеть BOOST.
В противном случае существует bunch библиотеки out там, в зависимости от потребностей вашего дерева.
Все контейнеры STL внешне представлены как "последовательности" с одним итерационным механизмом. Деревья не следуют этой идиоме.
Поскольку STL не является библиотекой "все". Он содержит, по существу, минимальные структуры, необходимые для создания вещей.
Этот выглядит многообещающим и, похоже, является тем, что вы ищете: http://tree.phi-sci.com/
ИМО, упущение. Но я думаю, что есть хорошая причина не включать структуру дерева в 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); }
} ;
Я думаю, есть несколько причин, почему нет 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
и т.д.
Мне было бы приятно, если бы это было в хорошей форме, чтобы найти свой путь в стандарт.
Все контейнеры STL могут использоваться с итераторами. Вы не можете иметь итератор дерево, потому что у вас нет пути "один правильный", который проходит через дерево.