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

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

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

Мне нужно дерево для хранения представления иерархии.

Roddy 18.11.2008 12:20

Я с парнем, который проголосовал против «правильных» ответов, что, кажется, так; «Деревья бесполезны». Есть важные, но неясные способы использования деревьев.

Joe Soul-bringer 22.12.2009 05:08

Думаю, причина банальна - в стандартной библиотеке еще никто не реализовал. Как будто в стандартной библиотеке до недавнего времени не было std::unordered_map и std::unordered_set. А до этого в стандартной библиотеке вообще не было STL-контейнеров.

doc 30.12.2016 05:34

Мои мысли (хотя я никогда не читал соответствующий стандарт, поэтому это комментарий, а не ответ) заключаются в том, что STL не заботится о конкретных структурах данных, он заботится о спецификациях, касающихся сложности и поддерживаемых операций. Таким образом, используемая базовая структура может варьироваться в зависимости от реализации и / или целевой архитектуры, при условии, что она удовлетворяет спецификациям. Я почти уверен, что std::map и std::set будут использовать дерево в каждой реализации, но они не обязаны это делать, если некоторая не древовидная структура также соответствует спецификациям.

Mark K Cowan 28.07.2017 00:22
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
390
4
218 546
14
Перейти к ответу Данный вопрос помечен как решенный

Ответы 14

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

В общем, если вам нужны базовые функциональные возможности библиотеки, которых нет в stl, исправление состоит в том, чтобы посмотреть на СПОСОБСТВОВАТЬ РОСТУ.

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

std :: map основана на красное черное дерево. Вы также можете использовать другие контейнеры, чтобы помочь вам реализовать свои собственные типы деревьев.

Обычно он использует красно-черные деревья (этого делать не обязательно).

Martin York 15.10.2008 23:11

GCC использует дерево для реализации карты. Кто-нибудь хочет посмотреть в свой каталог VC include, чтобы узнать, что использует Microsoft?

J.J. 16.10.2008 01:12

// Класс красно-черного дерева, предназначенный для использования // при реализации ассоциативных контейнеров STL (set, multiset, map и multimap). Взял это из моего файла stl_tree.h.

J.J. 16.10.2008 01:15

@ J.J. По крайней мере, в Studio 2010 он использует внутренний класс ordered red-black tree of {key, mapped} values, unique keys, определенный в <xtree>. В настоящий момент у вас нет доступа к более современной версии.

Justin Time - Reinstate Monica 16.06.2016 00:16
Ответ принят как подходящий

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

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

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

  • std::mapstd::multimap)
  • std::setstd::multiset)

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

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

Есть много-много причин использовать дерево, даже если они самые распространенные. Самый обычный! Равный всем.

Joe Soul-bringer 22.12.2009 05:06

Третья основная причина, по которой нужно дерево, - это всегда отсортированный список с быстрой вставкой / удалением, но для этого есть std: multiset.

VoidStar 26.02.2012 14:09

Как мы можем использовать карту для древовидной структуры данных, если мы не знаем глубины дерева?

NDestiny 12.06.2015 11:56

@Durga: Не уверен, насколько важна глубина, когда вы используете карту в качестве отсортированного контейнера. Карта гарантирует вставку / удаление / поиск журнала (n) (и содержит элементы в отсортированном порядке). Это все, что карта используется и реализована (обычно) в виде красного / черного дерева. Красное / черное дерево гарантирует, что дерево сбалансировано. Таким образом, глубина дерева напрямую связана с количеством элементов в дереве.

Martin York 09.08.2015 19:03

Я не согласен с этим ответом как в 2008 году, так и сейчас. Стандартная библиотека не имеет ускорения, и наличие чего-либо в ускорении не должно быть (и не было) причиной не принимать это в стандарт. Кроме того, BGL является общим и достаточно сложным, чтобы заслужить независимые от него специализированные классы деревьев. Кроме того, тот факт, что для std :: map и std :: set требуется дерево, является, IMO, еще одним аргументом в пользу наличия stl::red_black_tree и т. д. Наконец, деревья std::map и std::set сбалансированы, а std::tree может не быть.

einpoklum 26.07.2016 18:59

@einpoklum: «наличие чего-то в усилении не должно быть причиной не принимать это в стандарт» - учитывая, что один из цели повышения должен выступать в качестве испытательного полигона для полезных библиотек перед включением в стандарт, я могу только сказать «абсолютно!».

Martin Bonner supports Monica 09.01.2018 20:54

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

Двоичные деревья представляют собой чрезвычайно базовую функциональность и, по сути, более простую, чем другие контейнеры, поскольку такие типы, как std :: map, std :: multimap и stl :: set. Поскольку эти типы основаны на них, можно ожидать, что будет открыт базовый тип.

Catskul 06.05.2011 22:04

Я не думаю, что OP запрашивает дерево двоичный, он запрашивает дерево для хранения иерархии.

Mooing Duck 30.09.2013 09:00

Мало того, добавление «контейнера» дерева в STL означало бы добавление множества новых концепций, например, древовидный навигатор (обобщающий итератор).

alfC 17.08.2016 04:16

«Минимальные конструкции для создания вещей» - очень субъективное утверждение. Вы можете создавать что-то, используя необработанные концепции C++, поэтому я предполагаю, что истинным минимумом будет вообще отсутствие STL.

doc 30.12.2016 05:29

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

Некоторые вопросы для рассмотрения:

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

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

Вот несколько других общих реализаций дерева:

"... нет хорошего способа удовлетворить всех ..." За исключением того, что, поскольку stl :: map, stl :: multimap и stl :: set основаны на rb_tree stl, он должен удовлетворять такому же количеству случаев, как и эти базовые типы .

Catskul 06.05.2011 22:06

Учитывая, что нет возможности получить дочерние элементы узла std::map, я бы не стал называть эти древовидные контейнеры. Это ассоциативные контейнеры, которые обычно реализуются в виде деревьев. Большая разница.

Mooing Duck 30.09.2013 08:53

Я согласен с Mooing Duck, как бы вы реализовали поиск в ширину на std :: map? Это будет ужасно дорого

Marco A. 24.02.2014 19:34

Я начал использовать tree.hh Kasper Peeters, однако после ознакомления с условиями лицензирования GPLv3 или любой другой версии GPL это могло загрязнить наше коммерческое программное обеспечение. Я бы порекомендовал взглянуть на дерево деревьев, приведенное в комментарии @hplbsh, если вам нужна структура для коммерческих целей.

Jake88 24.02.2014 20:34

Фактически, Boost предоставляет контейнер в виде дерева. Библиотека называется Boost.PropertyTree.

Wildcat 22.11.2014 11:24

В итоге я использовал datasoftsolutions.net/tree_container_library/overview.php, я пробовал treeree, но документация была скудной, и использование было очень сложным. Boost.PropertyTree хорошо работает со строками, но не видел примеров использования чего-либо еще. TCL поддерживает только заголовки, коммерческую и STL. Это хорошо работает для моих целей.

Tom 18.12.2014 01:20

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

André 29.07.2015 14:26

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

Justin Time - Reinstate Monica 16.06.2016 00:10

Библиотека Boost Graph предоставляет R-дерево.

Edward Kirton 14.12.2016 03:58

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

Я не думаю, что он говорит о реализациях контейнеров, он говорит о самом реальном контейнере дерева.

Mooing Duck 30.09.2013 08:55

@MooingDuck Я думаю, что Wilhelmtell имеет в виду, что стандартная библиотека C++ не определяет контейнеры на основе их базовой структуры данных; он определяет контейнеры только по их интерфейсу и наблюдаемым характеристикам, таким как асимптотическая производительность. Когда вы думаете об этом, дерево на самом деле вовсе не контейнер (как мы их знаем). У них даже нет простых end() и begin(), с помощью которых вы можете перебирать все элементы и т. д.

Jordan Melo 26.11.2015 23:43

@JordanMelo: Ерунда по всем пунктам. Это вещь, которая содержит предметы. Очень просто создать в нем итераторы begin () и end (), а также двунаправленные итераторы для выполнения итерации. Каждый контейнер имеет разные характеристики. Было бы полезно, если бы можно было дополнительно иметь характеристики дерева. Должно быть довольно легко.

Mooing Duck 27.11.2015 07:20

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

doc 26.12.2016 05:43

@JordanMelo: С этой точки зрения адаптеры, такие как очереди, стеки или очереди с приоритетом, не будут принадлежать STL (у них также нет begin() и end()). И помните, что очередь с приоритетами обычно представляет собой кучу, которая, по крайней мере, теоретически представляет собой дерево (даже при фактических реализациях). Таким образом, даже если вы реализовали дерево в качестве адаптера с использованием другой базовой структуры данных, его можно было бы включить в STL.

andreee 10.01.2019 17:58

@andreee Конечно, никто не говорит, что только контейнеры принадлежат STL. Адаптеры контейнера не подходят для Концепция Container, и они, очевидно, находятся в STL, как вы заметили. Вы приводите priority_queue в качестве примера, но я думаю, вы согласитесь с тем, что он не предоставляет никакого древовидного поведения и использует древовидную структуру для очень конкретной цели. Но как насчет общих деревьев? Помните, что одна из наиболее важных функций древовидной структуры - моделирование отношений, которые не всегда соответствуют двоичной древовидной структуре.

Jordan Melo 13.01.2019 01:40

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

Как ни странно, это единственный ответ, который действительно отвечает на исходный вопрос.

Catskul 06.05.2011 22:01

Учитывая, что он хочет «иерархии», можно с уверенностью предположить, что что-либо с «балансировкой» - неправильный ответ.

Mooing Duck 30.09.2013 08:55

«Это внутренний файл заголовка, включенный в заголовки других библиотек. Вы не должны пытаться использовать его напрямую».

Dan 23.02.2015 03:57

@Dan: Копирование не означает его непосредственное использование.

einpoklum 26.07.2016 19:05

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

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

Древовидная структура данных может обеспечивать предварительный, неупорядоченный или постзаказный обход с помощью итераторов. Фактически это то, что делает std :: map.

Andrew Tomazos 23.09.2012 08:46

Да и нет ... все зависит от того, что вы подразумеваете под «деревом». std::map внутренне реализован как btree, но внешне выглядит как отсортированная ПОСЛЕДОВАТЕЛЬНОСТЬ ПАР. Учитывая любой элемент, вы можете универсально спросить, кто до, а кто после. Общие древовидные структуры, содержащие элементы, каждый из которых содержит другие, не налагают никакой сортировки или направления. Вы можете определить итераторы, которые обходят древовидную структуру разными способами (желтый | глубокий первый | последний ...), но как только вы это сделаете, контейнер std::tree должен вернуть один из них из функции begin. И нет явных причин возвращать то или иное.

Emilio Garavaglia 23.09.2012 17:41

Std :: map обычно представлена ​​сбалансированным двоичным деревом поиска, а не B-деревом. Тот же аргумент, который вы сделали, можно применить к std :: unordered_set, он не имеет естественного порядка, но имеет итераторы начала и конца. Требование начала и конца состоит в том, чтобы он выполнял итерацию всех элементов в некотором детерминированном порядке, а не в естественном порядке. preorder - вполне допустимый порядок итераций для дерева.

Andrew Tomazos 23.09.2012 18:22

«Std :: map обычно представляет собой сбалансированное двоичное дерево поиска, а не B-дерево.»: В какую игру вы играете? btree - это реализация двоичного дерева поиска, а map не "представлена" как таковая. он «РЕАЛИЗОВАН» как таковой. он ПРЕДСТАВЛЯЕТСЯ как отсортированная последовательность пар, которая начинается с begin () и заканчивается непосредственно перед end (). Верно, что вы можете пройти по дереву, начиная с некоторого begin () до некоторого end (), но, если не определить для него цель (как это делает map), больше нет подсказки, чем иметь элементы в списке.

Emilio Garavaglia 23.09.2012 19:21

Из вашего ответа следует, что нет структуры данных stl n-tree, потому что у нее нет интерфейса "последовательности". Это просто неверно.

Andrew Tomazos 23.09.2012 22:11

Нет: потому что у него нет УНИКАЛЬНОГО СПОСОБА для определения интерфейса последовательности, поскольку его обычная цель - не быть простой последовательностью. Принятие одного из них как «стандартного» лишит возможности иметь такую ​​структуру, а не навязывание этого сделает конструкцию бесполезной по отношению к <algorithm>, которому соответствует любой другой контейнер. Вы всегда можете сделать это самостоятельно, но то, как вы это сделаете, будет противоречить чужому, и нет никаких технических причин отдавать предпочтение тому или иному. Так что не может быть «стандартного» способа.

Emilio Garavaglia 24.09.2012 11:12

@EmiloGaravaglia: Как свидетельствует std::unordered_set, у которого нет «уникального способа» итерации своих членов (на самом деле порядок итерации псевдослучайный и определен в реализации), но все же является контейнером stl - это опровергает вашу точку зрения. Итерация по каждому элементу в контейнере по-прежнему является полезной операцией, даже если порядок не определен.

Andrew Tomazos 25.09.2012 07:10

Я больше не могу понять, не могу ли я объяснить или вы не хотите понимать. unordered_set не имеет уникального способа СОРТИРОВКИ, но имеет уникальный способ ХОДИТЬ: начать с begin() и закончить end(), и может быть подвержен любому <algorithm>. Но дерево с уникальным способом ХОДИТЬ бесполезно, так как цель дерева - ХОДИТЬ по крайней мере вверх и влево-вправо. Если моя манера говорить вам непонятна, прочтите это: stackoverflow.com/a/206011/924727 Это в точности моя концепция, с другой формулировкой.

Emilio Garavaglia 25.09.2012 11:28

Нет причин, по которым у контейнера должен быть один путь обхода, можно предлагать разные порядки итераций для разных типов пар итераторов (например, см. rbegin / rend). В std: tree может быть что-то вроде breadth_begin / breadth_end и depth_begin / depth_end для порядков вверх-вниз и влево-вправо соответственно. Ответ, на который вы ссылаетесь, правильный, это большая разница в потенциальной структуре, которая приводит к тому, что она не входит в стандартную библиотеку, но это не имеет ничего общего с «последовательностями» или «механизмом одной итерации».

Andrew Tomazos 25.09.2012 11:58

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

Emilio Garavaglia 25.09.2012 13:09

@EmilioGaravaglia Я понимаю вашу точку зрения. Вы хотите иметь возможность выбирать стратегию итерации в контейнере дерева, потому что а) дерево семантически соответствует вашим данным б) вы хотите пройти по дереву в конкретном случае для решения проблемы. Возможное решение: std::tree имеет аргумент шаблона перечислимого тега, который сообщает порядок обхода по умолчанию, заданный функцией begin () для диапазона, скажем. По умолчанию в этом шаблоне arg указано traversal::inorder. Вы даже можете дать имена другим: template<typename NodeT> using tree_pre<NoteT> = tree<NodeT, traversal::preorder>; и т. д. Это работоспособная проблема.

emsr 05.08.2015 23:16

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

emsr 05.08.2015 23:39

@emsr: Хорошее предложение. Конечно, через 3 года что-то развивается, но ... на самом деле он ввел новую концепцию (диапазоны), фактически позволяющую разрушить ограничение.

Emilio Garavaglia 06.08.2015 22:53

ИМО, упущение. Но я думаю, что есть веская причина не включать древовидную структуру в 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); } 
} ;

У вас есть много сырых указателей, многие из которых вообще не нуждаются в указателях.

Mooing Duck 30.09.2013 09:02

Предлагаю вам отозвать этот ответ. Класс TreeNode является частью реализации дерева.

einpoklum 26.07.2016 19:08

"I want to store a hierarchy of objects as a tree"

C++ 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, тогда все может усложниться. Вы можете создать свои собственные итераторы и добиться некоторой совместимости, однако многие алгоритмы просто не имеют никакого смысла для иерархии (например, всего, что меняет порядок диапазона). Даже определение диапазон в иерархии может быть беспорядочным делом.

Если проект может разрешить сортировку дочерних элементов tree_node, то использование std :: set <> вместо std :: vector <> и последующее добавление оператора <() к объекту tree_node значительно улучшит «поисковое» выполнение Т-образного объекта.

J Jorgenson 09.10.2013 23:04

Оказывается, они поленились и фактически создали ваш первый пример Undefined Behavior.

user541686 12.08.2014 11:03

@Mehrdad: Я наконец решил спросить подробности вашего комментария здесь.

Brent Bradburn 09.09.2015 22:47

many of the algorithms simply don't make any sense for a hierarchy. Вопрос интерпретации. Представьте себе структуру пользователей stackoverflow, и каждый год вы хотите, чтобы те, у кого больше очков репутации, руководили теми, у кого меньше очков репутации. Таким образом, обеспечивая итератор BFS и соответствующее сравнение, вы каждый год просто запускаете std::sort(tree.begin(), tree.end()).

doc 30.12.2016 06:04

По тому же принципу вы можете легко построить ассоциативное дерево (для моделирования неструктурированных записей типа "ключ-значение", например, JSON), заменив vector на map в приведенном выше примере. Для полной поддержки структуры, подобной JSON, вы можете использовать variant для определения узлов.

Brent Bradburn 19.08.2019 19:31

Также тривиально легко создать собственную реализацию vector поверх new / delete, но у нас есть и почти все используют std::vector ...

fwyzard 16.01.2021 17:09

Все контейнеры STL можно использовать с итераторами. У вас не может быть итератора в дереве, потому что у вас нет «единственного правильного» способа пройти по дереву.

Но вы можете сказать, что BFS или DFS - правильный путь. Или поддержите их обоих. Или любой другой, который вы можете себе представить. Просто скажите пользователю, что это такое.

tomas789 21.10.2013 11:36

в std :: map есть итератор дерева.

Jai 04.09.2015 14:46

Дерево может определять свой собственный тип итератора, который проходит все узлы в порядке от одного «крайнего» к другому (то есть для любого двоичного дерева с путями 0 и 1 оно может предлагать итератор, который идет от «все 0» до «все». 1s ", и обратный итератор, который делает противоположное; например, для дерева с глубиной 3 и начальным узлом s он может перебирать узлы как s000, s00, s001, s0, s010, s01, s011, s. , s100, s10, s101, s1, s110, s11, s111 (от «крайнего левого» до «крайнего правого»); он также может использовать шаблон обхода глубины (s, s0, s1, s00, s01, s10, s11,

Justin Time - Reinstate Monica 16.06.2016 00:32

и т. д.) или какой-либо другой шаблон, если он проходит по каждому узлу таким образом, что каждый из них передается только один раз.

Justin Time - Reinstate Monica 16.06.2016 00:33

Я не понимаю, почему этот ответ был так сильно отвергнут. Это единственный ответ на вопрос. Дерево не является частью интерфейса STL (хотя оно существует внутри), поскольку дерево не является контейнером в смысле STL, то есть не является контейнером элементов с линейной итерацией. (Например, у него не будет уникальных begin() и end()). Добавление дерева в STL означало бы добавление множества новых концепций, например, древовидный навигатор (обобщающий итератор).

alfC 17.08.2016 04:14

@alfC, что не так с определением begin() как корневого узла и end() как последнего листа?

doc 26.12.2016 06:01

@doc, конечно, есть много листьев, и даже если вы выберете один в качестве последнего, нет единственного способа пройти три линейно.

alfC 26.12.2016 13:54

@alfC вы можете выбрать любую из DFS или BFS, как уже указывал tomas789. Я много раз реализовывал итераторы для своих древовидных структур.

doc 27.12.2016 01:24

@doc, конечно, но трансверсаль не уникальна, как в последовательности. Общий график может быть пересечен по DFS или BFS, но это еще не делает их последовательностью. Их можно рассматривать как последовательность, но это другое дело.

alfC 27.12.2016 02:29

Библиотека @alfC std не ограничена последовательностями, std::unordered_map не является последовательностью, но уже существует в стандартной библиотеке. Не существует таких понятий, как «уникальная итерация» или «уникальный обход». Даже с массивом вы можете перебирать его любым способом. Итераторы точно решают эту проблему и предоставляют уровень абстракции, поэтому вы можете выполнять итерацию по таким структурам, как std::map. Все дело в условности.

doc 30.12.2016 04:58

@doc, очень хорошее замечание. Я думаю, что std::unordered_set был «сделан» последовательностью, потому что мы не знаем лучшего способа итерации по элементам, кроме как каким-то произвольным способом (внутренне заданным хеш-функцией). Я думаю, что это противоположный случай с деревом: итерация по unordered_set недооценена, теоретически нет «никакого способа» определить итерацию, кроме, возможно, «случайным образом». В случае с деревом существует множество «хороших» (неслучайных) способов. Но, опять же, ваша точка зрения верна.

alfC 30.12.2016 08:46

Я думаю, что нет 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 должен быть хорошо написан - так, чтобы пустой набор, вектор или контейнер списка был действительно пустой от какой-либо полезной нагрузки в случае по умолчанию.

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

Однако помимо деревьев есть еще и «соседи»; Деревья, расположенные выше, обладают тем свойством, что, удаляя корень, вы удаляете все.

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

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

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

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

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

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

Имея это в виду, я попытался разработать свою собственную древовидную структуру данных, которая будет предоставлять интерфейс, подобный STL, и будет максимально использоваться с существующими алгоритмами STL.

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

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

Вот что мне удалось придумать: https://github.com/cppfw/utki/blob/master/src/utki/tree.hpp

А вот и тесты: https://github.com/cppfw/utki/blob/master/tests/tree/tests.cpp

Другие вопросы по теме