Есть некоторые структуры данных, которые действительно полезны, но неизвестны большинству программистов. Какие они?
Все знают о связанных списках, бинарных деревьях и хешах, но как насчет Пропустить списки и фильтры Блума, например. Я хотел бы узнать больше о структурах данных, которые не так распространены, но которые стоит знать, потому что они опираются на великие идеи и обогащают инструментарий программиста.
PS: мне также интересны такие методы, как « Танцевальные ссылки», которые умно используют свойства общей структуры данных.
РЕДАКТИРОВАТЬ : Пожалуйста, попробуйте включить ссылки на страницы, описывающие структуры данных более подробно. Кроме того, попробуйте добавить пару слов о том, почему структура данных клевая (как уже указывал Йонас Кёлькер ). Кроме того, попробуйте предоставить одну структуру данных для каждого ответа . Это позволит лучшим структурам данных перемещаться к вершине на основе только их голосов.
Ответы:
Попытки , также известные как префиксные деревья или критические биты , существуют уже более 40 лет, но все еще относительно неизвестны. Очень крутое использование попыток описано в « TRASH - динамическая структура данных дерева и хеш-памяти LC », в которой объединяется дерево с хэш-функцией.
источник
Фильтр Блума : битовый массив из m битов, изначально все установлены в 0.
Чтобы добавить элемент, вы запускаете его через k хеш-функций, которые дадут вам k индексов в массиве, который вы затем установите в 1.
Чтобы проверить, есть ли элемент в наборе, вычислите k индексов и проверьте, все ли они установлены в 1.
Конечно, это дает некоторую вероятность ложных срабатываний (согласно википедии это около 0,61 ^ (m / n), где n - количество вставленных элементов). Ложные негативы невозможны.
Удаление элемента невозможно, но вы можете реализовать фильтр подсчета Блума, представленный массивом целых чисел и увеличением / уменьшением.
источник
Веревка : это строка, которая учитывает дешевые prepends, подстроки, средние вставки и добавления. Я действительно использовал его только один раз, но никакой другой структуры не хватило бы. Обычные строки и массивы prepends были слишком дорогими для того, что нам нужно было сделать, и об обратном ничего не могло быть и речи.
источник
Пропускать списки довольно аккуратно.
Они могут использоваться в качестве альтернативы сбалансированным деревьям (используя пробалистическое балансирование, а не строгое соблюдение баланса). Их легко реализовать и быстрее, чем, скажем, красно-черное дерево. Я думаю, что они должны быть в каждом хорошем инструменте программистов.
Если вы хотите получить подробное представление о пропускаемых списках, то здесь есть ссылка на видео лекции MIT «Введение в алгоритмы».
Также здесь представлен Java-апплет, наглядно демонстрирующий Skip Lists.
источник
Пространственные индексы , в частности R-деревья и KD-деревья , эффективно хранят пространственные данные. Они хороши для координатных данных географической карты и алгоритмов определения местоположения и маршрута VLSI, а иногда и для поиска ближайших соседей.
Битовые массивы компактно хранят отдельные биты и позволяют выполнять быстрые битовые операции.
источник
Молнии - производные структур данных, которые изменяют структуру, чтобы иметь естественное понятие «курсор» - текущее местоположение. Они действительно полезны, поскольку гарантируют, что индикаторы не могут быть вне границ - использованы, например, в оконном менеджере xmonad для отслеживания того, какое окно было сфокусировано.
Удивительно, но вы можете получить их, применяя методы из исчисления к типу исходной структуры данных!
источник
Вот несколько из них:
Суффикс пытается. Полезно для почти всех видов поиска строк (http://en.wikipedia.org/wiki/Suffix_trie#Functionality ). Смотрите также массивы суффиксов; они не такие быстрые, как суффиксные деревья, но намного меньше.
Splay деревья (как упомянуто выше). Причина, по которой они классные, тройная:
Упорядоченные кучи деревья поиска: вы храните в дереве несколько пар (key, prio), так что это дерево поиска по ключам и упорядочено по куче относительно приоритетов. Можно показать, что такое дерево имеет уникальную форму (и оно не всегда полностью упаковано слева направо). При случайных приоритетах он дает ожидаемое время поиска O (log n), IIRC.
Ниша - списки смежности для неориентированных плоских графов с O (1) соседними запросами. Это не столько структура данных, сколько особый способ организации существующей структуры данных. Вот как это делается: у каждого плоского графа есть узел со степенью не более 6. Выберите такой узел, поместите его соседей в список соседей, удалите его из графа и выполняйте рекурсию до тех пор, пока граф не станет пустым. Когда дана пара (u, v), ищите u в списке соседей v и v в списке соседей u. Оба имеют размер не более 6, так что это O (1).
По приведенному выше алгоритму, если u и v являются соседями, у вас не будет и u в списке v, и v в списке u. Если вам это нужно, просто добавьте отсутствующих соседей каждого узла в список соседей этого узла, но сохраните, какую часть списка соседей вам нужно просмотреть для быстрого поиска.
источник
Я думаю, что альтернативы стандартным структурам данных без блокировок, т. Е. Очередь без блокировки, стек и список, остаются без внимания.
Они становятся все более актуальными, поскольку параллелизм становится более высоким приоритетом и является гораздо более замечательной целью, чем использование мьютексов или блокировок для обработки одновременных операций чтения / записи.
Вот несколько ссылок
http://www.cl.cam.ac.uk/research/srg/netos/lock-free/
http://www.research.ibm.com/people/m/michael/podc-1996.pdf [Ссылки на PDF]
http://www.boyet.com/Articles/LockfreeStack.html
Блог Майка Актона (часто провокационный) содержит несколько отличных статей о дизайне и подходах без блокировок.
источник
Я думаю, что Disjoint Set довольно изящен для случаев, когда вам нужно разделить группу элементов на отдельные наборы и запросить членство. Хорошая реализация операций Union и Find приводит к амортизированным затратам, которые фактически постоянны (обратная функция Ackermnan, если я правильно помню класс структур данных).
источник
Кучи Фибоначчи
Они используются в некоторых из самых быстрых известных алгоритмов (асимптотически) для многих задач, связанных с графами, таких как проблема кратчайшего пути. Алгоритм Дейкстры выполняется за O (E log V) со стандартными двоичными кучами; использование куч Фибоначчи улучшает это до O (E + V log V), что является огромным ускорением для плотных графов. К сожалению, тем не менее, они имеют высокий постоянный коэффициент, что часто делает их непрактичными на практике.
источник
Любой, кто имеет опыт работы с 3D-рендерингом, должен быть знаком с деревьями BSP . Как правило, это метод структурирования трехмерной сцены, который должен быть управляемым для рендеринга, зная координаты камеры и направление.
источник
Деревья Хаффмана - используются для сжатия.
источник
Посмотрите на Finger Trees , особенно если вы поклонник ранее упомянутых чисто функциональных структур данных. Они являются функциональным представлением постоянных последовательностей, поддерживающих доступ к концам в амортизированном постоянном времени, а также конкатенацию и расщепление во времени, логарифмическое по размеру меньшего куска.
Согласно оригинальной статье :
Дерево пальца может быть параметризовано с помощью моноида , и использование разных моноидов приведет к разному поведению дерева. Это позволяет Finger Trees моделировать другие структуры данных.
источник
Круговой или кольцевой буфер - используется для потоковой передачи, помимо прочего.
источник
Я удивлен, что никто не упомянул деревья Меркле (то есть деревья хеша ).
Используется во многих случаях (программы P2P, цифровые подписи), когда вы хотите проверить хеш целого файла, когда вам доступна только часть файла.
источник
Думаю, было бы полезно узнать, почему они крутые. В общем, вопрос «почему» важнее всего задать;)
Мой ответ состоит в том, что они предоставляют вам O (log log n) словарей с ключами {1..n}, независимо от того, сколько ключей используется. Точно так же, как повторное деление пополам дает O (log n), повторное sqrting дает O (log log n), что происходит в дереве vEB.
источник
Как насчет деревьев сплайс ?
Кроме того, в голову приходят исключительно функциональные структуры данных Криса Окасаки .
источник
Интересный вариант хеш-таблицы называется Cuckoo Hashing . Он использует несколько хеш-функций вместо 1, чтобы иметь дело с хеш-коллизиями. Столкновения разрешаются путем удаления старого объекта из местоположения, указанного в основном хеше, и перемещения его в местоположение, указанное альтернативной хэш-функцией. Хэширование с кукушкой позволяет более эффективно использовать пространство памяти, поскольку вы можете увеличить коэффициент загрузки до 91% с помощью всего лишь 3 хэш-функций и при этом иметь хорошее время доступа.
источник
Мин-макс куча является вариацией кучи , которая реализует очередь раздвоенного приоритета. Это достигается простым изменением свойства кучи: дерево называется минимально-упорядоченным, если каждый элемент на четных (нечетных) уровнях меньше (больше), чем все дочерние элементы и внуки. Уровни нумеруются начиная с 1.
http://internet512.chonbuk.ac.kr/datastructure/heap/img/heap8.jpg
источник
Мне нравятся структуры данных Cache Oblivious . Основная идея состоит в том, чтобы выстроить дерево в рекурсивно меньшие блоки, чтобы кэши разных размеров использовали преимущества блоков, которые в них удобно помещались. Это приводит к эффективному использованию кэширования во всем: от кэша L1 в ОЗУ до больших объемов данных, считываемых с диска, без необходимости знать специфику размеров любого из этих слоев кэширования.
источник
Слева склоняются красно-черные деревья . Значительно упрощенная реализация красно-черных деревьев Роберта Седжвика, опубликованная в 2008 году (~ половина строк кода для реализации). Если вам когда-либо приходилось сталкиваться с реализацией красно-черного дерева, прочитайте об этом варианте.
Очень похоже (если не идентично) на деревья Андерссона.
источник
Очередь за работой
Безблокировочная структура данных для равномерного распределения работы между несколькими потоками. Реализация очереди на кражу работы в C / C ++?
источник
Герт Стёлтинг Бродал и Крис Окасаки загрузили косые биномиальные кучи :
Несмотря на их длинное имя, они обеспечивают асимптотически оптимальные операции с кучей даже в настройках функций.
O(1)
размер, соединение , вставка, минимумO(log n)
deleteMinОбратите внимание, что объединение занимает,
O(1)
а неO(log n)
время, в отличие от более известных куч, которые обычно рассматриваются в учебниках по структуре данных, таких как левые кучи . И в отличие от кучи Фибоначчи , эти асимптотики являются наихудшими, а не амортизируются, даже если используются постоянно!В Haskell есть несколько реализаций .
Они были совместно получены Бродалом и Окасаки после того, как Бродал придумал императивную кучу с такими же асимптотиками.
источник
Большинство из них, если не все, описаны в Словаре алгоритмов и структур данных NIST.
источник
Шаровые деревья. Просто потому, что они заставляют людей хихикать.
Шариковое дерево - это структура данных, которая индексирует точки в метрическом пространстве. Вот статья о их создании. Они часто используются для нахождения ближайших соседей к точке или ускорения k-средних.
источник
Не совсем структура данных; это еще один способ оптимизации динамически распределенных массивов, но буферы гэпов, используемые в Emacs, довольно крутые.
источник
Дерево Фенвика. Это структура данных, позволяющая вести подсчет суммы всех элементов вектора между двумя заданными субиндексами i и j. Тривиальное решение, предварительно вычисляющее сумму, так как начало не позволяет обновить элемент (вы должны выполнить O (n) работу, чтобы не отставать).
Деревья Фенвика позволяют обновлять и запрашивать в O (log n), и как это работает, действительно круто и просто. Это действительно хорошо объяснено в оригинальной статье Фенвика, свободно доступной здесь:
http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol24/issue3/spe884.pdf
Его отец, дерево RQM, также очень круто: оно позволяет хранить информацию о минимальном элементе между двумя индексами вектора, и оно также работает в O (log n) update и query. Мне нравится преподавать сначала RQM, а затем Fenwick Tree.
источник
Ван Эмде-Боас деревья . У меня есть даже C ++ реализации этого, для детей до 2 ^ 20 целых чисел.
источник
Вложенные множества удобны для представления деревьев в реляционных базах данных и выполнения запросов к ним. Например, ActiveRecord (ORM по умолчанию в Ruby on Rails) поставляется с очень простым плагином для вложенных множеств , который делает работу с деревьями тривиальной.
источник
Это довольно специфично для предметной области, но структура данных с половинными краями довольно аккуратна. Он обеспечивает способ перебора многоугольников (граней и ребер), что очень полезно в компьютерной графике и вычислительной геометрии.
источник