Які менш відомі, але корисні структури даних?


795

Навколо є деякі структури даних, які справді корисні, але невідомі більшості програмістів. Які вони?

Всі знають про пов’язані списки, бінарні дерева та хеші, але як щодо пропускних списків та фільтрів Bloom, наприклад. Мені хотілося б дізнатися більше структур даних, які не так поширені, але їх варто знати, оскільки вони покладаються на чудові ідеї та збагачують інструментальну скриньку програміста.

PS: Мене також цікавлять такі методи, як Танцювальні посилання, які розумно використовують властивості загальної структури даних.

EDIT : Будь ласка, спробуйте включити посилання на сторінки, що описують структури даних більш детально. Крім того, спробуйте додати пару слів про те, чому структура даних крута (як вже вказував Йонас Келькер ). Також спробуйте надати одну структуру даних за кожну відповідь . Це дозволить кращим структурам даних плисти до вершини лише на основі їх голосів.


Відповіді:


271

Випробування , також відомі як дерева-префікси або дерева- критики , існують понад 40 років, але до цих пір відносно невідомі. Дуже круте використання проб описано в " TRASH - Динамічна структура даних LC-trie та хеш ", яка поєднує трие з хеш-функцією.


12
дуже часто використовуються перевіряючі орфографії
Стівен А. Лоу

Цікаві спроби також є цікавим варіантом, коли ви використовуєте лише префікс рядків як вузли та іншим чином зберігаєте списки рядків у вузлах.
Торстен Марек

Регекс-двигун в Perl 5.10 автоматично створює спроби.
Бред Гілберт

На мій досвід, спроби коштують болісно, ​​враховуючи, що вказівник, як правило, довший, ніж знак, що шкода. Вони підходять лише для певних наборів даних.
Джо

18
Оскільки жодне питання щодо ТА, незалежно від теми, не є повним, без того, щоб хто-небудь згадував jQuery .... Джон Ресіг, творець jQuery, має цікавий ряд даних про структуру даних, де він розглядає різні реалізації трійки серед інших: ejohn.org/blog/ переглянуто-javascript-словник-пошук
Oskar Austegard

231

Фільтр Блум : Бітовий масив m біт, спочатку всі встановлені на 0.

Щоб додати елемент, ви запустите його через k хеш-функції, які дадуть вам k індекси в масиві, який ви встановите на 1.

Щоб перевірити, чи є елемент у наборі, обчисліть k індекси та перевірте, чи всі вони встановлені на 1.

Звичайно, це дає певну ймовірність хибнопозитивних результатів (згідно з wikipedia це приблизно 0,61 ^ (m / n), де n - кількість вставлених елементів). Неправдиві негативи неможливі.

Видалення елемента неможливо, але ви можете реалізувати фільтр підрахунку цвітіння , представлений масивом ints та збільшенням / зменшенням.


20
Ви забудете згадати їх використання зі словниками :) Ви можете видавити повний словник у фільтр із розквітанням приблизно 512 кб, як хештел без значень
Chris S

8
Google цитує використання фільтрів Bloom для впровадження BigTable.
Брайан Джанфоркаро

16
@FreshCode Це фактично дозволяє вам дешево протестувати відсутність елемента в наборі, оскільки ви можете отримати помилкові позитиви, але ніколи неправдиві негативи
Том Савадж,

26
@FreshCode Як сказав @Tom Savage, це корисніше при перевірці негативів. Наприклад, ви можете використовувати його як швидку і невелику (з точки зору використання пам'яті) перевірку орфографії. Додайте до нього всі слова, а потім спробуйте пошукати слова, які вводить користувач. Якщо ви отримаєте негатив, це означає, що його написано неправильно. Тоді ви можете запустити трохи дорожчу перевірку, щоб знайти найближчі збіги та запропонувати виправлення.
lacop

5
@ abhin4v: Фільтри Bloom часто використовуються, коли більшість запитів, ймовірно, повертають відповідь "ні" (як, наприклад, тут), це означає, що невелика кількість відповідей "так" можна перевірити повільнішим точним тестом. Це все ще призводить до значного скорочення середнього часу відповіді на запит. Не знаю, чи безпечний перегляд Chrome це робить, але це моє здогадування.
j_random_hacker

140

Мотузка : це струна, яка дозволяє отримати дешеві прикидки, підрядки, середні вставки та додатки. Я дійсно використовував його лише один раз, але жодна інша структура не була б достатньою. Звичайні рядки і масиви прикидаються просто надто дорогими для того, що нам потрібно було зробити, і перевертати все, що не було, не можна було говорити.


У мене були думки про щось подібне для власних цілей. Приємно знати, що це вже впроваджено десь в іншому місці.
Кіббі

15
Є реалізація в SGI STL (1998): sgi.com/tech/stl/Rope.html
кварк

2
Не знаючи, як називається, нещодавно я написав щось дуже схоже на це для Java - продуктивність була чудовою: code.google.com/p/mikeralib/source/browse/trunk/Mikera/src/…
mikera

Мотузка досить рідко: stackoverflow.com/questions/1863440 / ...
Will

6
Посилання Мікери несвіже, ось течія .
aptwebapps

128

Пропускні списки досить акуратні.

Вікіпедія
Пропускний список - це імовірнісна структура даних, що базується на декількох паралельних, упорядкованих зв'язаних списках, ефективність порівнянна з двійковим деревом пошуку (журнал порядку n середній час для більшості операцій).

Вони можуть бути використані як альтернатива збалансованим деревам (використовуючи пробалістичне врівноваження, а не суворе виконання рівноваги). Їх легко здійснити і швидше, ніж скажімо, червоно-чорне дерево. Я думаю, вони повинні бути у всіх інструментальних інструментів для хороших програмістів.

Якщо ви хочете отримати поглиблене вступ до пропускних списків, тут є посилання на відеоролик про вступ MIT до алгоритмів про них.

Також ось аплет Java, який наочно демонструє пропуск списків.


+1 Qt використовує пропускні списки, а не RB-дерева для своїх відсортованих карт та наборів. Так, вони вишукані (так чи інакше, в обов'язкових мовах).
Майкл Екстранд

2
Redis використовує пропускні списки для реалізації "Сортовані набори".
антирез

Пропускні списки, мабуть, є моєю улюбленою структурою даних, яку я можу використовувати, коли мені потрібна хороша структура даних, і я не маю гарантій щодо впорядкування даних, і я хочу простішої реалізації, ніж інші "збалансовані" структури даних. Така гарна річ.
earino

Цікава побічна примітка: Якщо ви додасте достатньо рівнів у свої пропущені списки, ви по суті опиняєтесь B-деревом.
Ріяд Калла

92

Просторові показники , зокрема R-дерева та KD-дерева , ефективно зберігають просторові дані. Вони гарні для географічних даних координат карти та алгоритмів місця та маршруту VLSI, а іноді і для пошуку найближчого сусіда.

Бітові масиви компактно зберігають окремі біти і дозволяють швидко виконувати бітові операції.


6
Просторові показники також корисні для моделювання N-тіла, що включає сили дальнього дії, як гравітація.
Джастін Піл

87

Блискавки - похідні структур даних, які модифікують структуру, щоб мати природне поняття «курсор» - поточне розташування. Вони справді корисні, оскільки гарантують, що показники не можуть бути використані, наприклад, у менеджері вікон xmonad для відстеження, яке вікно зосередилось.

Дивно, але їх можна отримати, застосувавши методи від обчислення до типу оригінальної структури даних!


2
це корисно лише у функціональному програмуванні (на імперативних мовах ви просто зберігаєте вказівник чи індекс). Крім того, я все ще не розумію, як блискавки дійсно працюють.
Стефан Монов

4
@Stefan в тому, що зараз вам не потрібно зберігати окремий індекс чи покажчик.
Дон Стюарт

69

Ось декілька:

  • Суфікс намагається. Корисно для майже всіх видів рядкового пошуку (http://en.wikipedia.org/wiki/Suffix_trie#Functionality ). Дивіться також суфіксні масиви; вони не такі швидкі, як суфіксні дерева, але в цілому набагато менші.

  • Погризаються дерева (як було сказано вище). Причина, в якій вони круті, потрійна:

    • Вони невеликі: вам потрібні лише лівий і правий вказівники, як у будь-якому бінарному дереві (жодна інформація про колір або розмір вузла не потрібно зберігати)
    • Вони (порівняно) дуже прості у здійсненні
    • Вони пропонують оптимальну амортизовану складність для цілої низки "критеріїв вимірювання" (час пошуку n log - той, кого всі знають). Подивитисяhttp://en.wikipedia.org/wiki/Splay_tree#Performance_theorems
  • Дерева пошуку впорядковані за купою: ви зберігаєте купу дерев (ключів, пріоритетів) у дереві, таким чином, що це дерево пошуку по відношенню до клавіш, і впорядковане по відношенню до пріоритетів. Можна показати, що таке дерево має унікальну форму (і воно не завжди повністю упаковане вліво-вліво). Із випадковими пріоритетами він дає очікуваний час пошуку (O n (log n)), IIRC.

  • Ніша - це списки суміжності для непрямих плоских графіків із запитом сусідів O (1). Це не стільки структура даних, скільки особливий спосіб організації існуючої структури даних. Ось як це зробити: кожен планарний графік має вузол із ступенем максимум 6. Виберіть такий вузол, поставте його сусідів у свій сусідній список, видаліть його з графіка та повторіть, поки графік не буде порожнім. Коли вам дають пару (u, v), шукайте u в списку сусідів v і в списку сусідів v. Обидва мають розмір не більше 6, тож це O (1).

За вищенаведеним алгоритмом, якщо u і v є сусідами, ви не будете мати і u v, і v у списку u. Якщо вам це потрібно, просто додайте відсутніх сусідів кожного вузла до списку сусідів цього вузла, але збережіть, скільки списку сусідів потрібно переглянути для швидкого пошуку.


Дерево пошуку, замовлене в Хіпі, називається treap. Один трюк, який ви можете зробити з цим, - це змінити пріоритет вузла, щоб натиснути його на дно дерева, де його легше видалити.
папірець

1
"Дерево пошуку, замовлене в Хіпі, називається treap." - У визначенні, яке я чув, IIRC, treap - це дерево впорядкованих впорядкованих груп із випадковими пріоритетами. Ви можете вибрати інші пріоритети, залежно від програми ...
Йонас Келькер,

2
Суфікс Trie майже , але не зовсім такий же , як багато суфіксів прохолодніше дерева , у якого є рядки , а не окремі букви по краях і може бути побудовані в лінійний час (!). Також, незважаючи на те, що це асимптотично повільніше, на практиці суфіксні масиви часто набагато швидше, ніж дерева суфіксів для багатьох завдань через їх менший розмір та меншу кількість покажчиків. Полюбіть плоский пошук графіка O (1) BTW!
j_random_hacker

@j_random_hacker: суфіксні масиви не асимптотично повільніші. Ось ~ 50 рядків коду для побудови лінійного масиву суфіксів: cs.helsinki.fi/u/tpkarkka/publications/icalp03.pdf
Edward KMETT

1
@Edward Kmett: Я насправді прочитав цей папір, це був досить прорив у побудові масиву суфіксів . (Хоча вже було відомо, що лінійна побудова часу можлива шляхом переходу «через» суфіксне дерево, це був перший, безперечно, практичний «прямий» алгоритм.) Але деякі операції поза побудовою все ще є асимптотично повільнішими за суфіксним масивом, якщо тільки LCA також будується стіл. Це також можна зробити в O (n), але ви втрачаєте переваги розміру та локальності чистого суфіксного масиву.
j_random_hacker

65

Я думаю, що альтернативи без блокування для стандартних структур даних, тобто черга без блокування, стек та список значно пропускаються.
Вони стають все більш актуальними, оскільки паралельність стає вищим пріоритетом і є набагато більш захоплюючою метою, ніж використання Mutexes або замків для обробки одночасного читання / запису.

Ось декілька посилань
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

Блог Майка Актона (часто провокаційний) має кілька чудових статей про дизайн та підходи без замків


Безблокові альтернативи настільки важливі в сьогоднішньому багатоядерному, дуже паралельному, масштабованому світі залежного :-)
earino

Що ж, руйнівник справді робить кращу роботу в більшості випадків.
deadalnix

55

Я думаю, що Disjoint Set досить чудовий для випадків, коли вам потрібно розділити купу елементів на різні набори та запитувати членство. Хороша реалізація операцій Union і Find призводить до амортизованих витрат, які фактично є постійними (обернено функцію Ackermnan, якщо я правильно згадую свій клас структур даних).


8
Це також називається "структура даних об'єднання-пошуку". Мені було в захваті, коли я вперше дізнався про цю розумну структуру даних у класі алгоритмів ...
BlueRaja - Danny Pflughoeft

розширення union-find-delete дозволяють також видаляти постійний час.
Пік

4
Я використовував Disjoint Set для свого генератора Dungeon, щоб переконатися, що всі кімнати доступні пасажами :)
goldenratio

52

Фібоначчі купи

Вони використовуються в деяких найшвидших відомих алгоритмах (асимптотично) для безлічі проблем, пов'язаних з графіком, таких як проблема найкоротшого шляху. Алгоритм Дейкстри працює в O (E log V) час зі стандартними бінарними купами; використання куб Фібоначчі покращує це до O (E + V log V), що є величезною швидкістю для щільних графіків. На жаль, вони мають високий постійний фактор, що часто робить їх непрактичними на практиці.


Високий постійний коефіцієнт, як ви сказали, і важко реалізувати за словами друга, який мав це зробити. Fianally не так круто, але все ж, можливо, варто знати.
p4bl0

Ці хлопці тут змусили їх працювати конкурентоспроможними порівняно з іншими видами купи: cphstl.dk/Presentation/SEA2010/SEA-10.pdf Існує пов'язана структура даних під назвою Pairing Heaps, що простіше в застосуванні і яка пропонує досить хороші практичні показники. Однак теоретичний аналіз частково відкритий.
Мануель

З мого досвіду роботи з кучами Фібоначчі я з’ясував, що дорога операція розподілу пам’яті робить її менш ефективною, ніж проста двійкова купа, заповнена масивом.
пришвидшене

44

Усі, хто має досвід 3D-рендерінгу, повинні ознайомитись із деревами BSP . Як правило, це метод шляхом структурування 3D-сцени для керування для відображення знань про координати камери та підшипник.

Розбиття бінарного простору (BSP) - метод рекурсивного підрозділу простору на опуклі множини гіперпланами. Цей підрозділ породжує представлення сцени за допомогою структури даних про дерево, відому як дерево BSP.

Іншими словами, це метод розбиття багатокутників хитромудрої форми на опуклі множини або менші багатокутники, що складаються цілком з нерефлекторних кутів (кути менше 180 °). Для більш загального опису розділення простору див. Розділ простору.

Спочатку такий підхід був запропонований у тривимірній комп'ютерній графіці для підвищення ефективності візуалізації. Деякі інші програми включають виконання геометричних операцій з фігурами (конструктивна суцільна геометрія) в САПР, виявлення зіткнень у робототехніці та 3D-комп’ютерних іграх та інші комп’ютерні програми, що передбачають обробку складних просторових сцен.


... та пов’язані з цим октриси та kd-дерева.
Lloeki


38

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

Відповідно до оригінальної статті :

Наші функціональні 2-3 пальчикові дерева - це зразок загальної методики дизайну, запровадженої Окасакі (1998), яка називається неявним рекурсивним уповільненням . Ми вже відзначали, що ці дерева є розширенням його неявної декельної структури, замінюючи пари з 2-3 вузлами, щоб забезпечити гнучкість, необхідну для ефективного конкатенації та розщеплення.

Дерево пальця можна параметризувати моноїдом , а використання різних моноїдів призведе до різної поведінки дерева. Це дозволяє пальцям імітувати інші структури даних.



Подивіться на цю повторювану відповідь , це варто прочитати!
Франсуа Г

34

Круглий або кільцевий буфер - використовується для потокового передавання, серед іншого.


4
Також огидно якось вдалося запатентувати (принаймні, коли використовується для відео). ip.com/patent/USRE36801
Девід Ейсон

Виходячи з читання посилання, я не думаю, що сама структура даних запатентована, але якийсь винахід заснований на ній. Я погоджуюся, що це, безумовно, дуже недостатньо використовувана структура даних.
Гравітація

33

Я здивований, що ніхто не згадував дерев Меркле (тобто дерев хешу ).

Використовується в багатьох випадках (програми P2P, цифрові підписи), коли ви хочете перевірити хеш цілого файлу, коли у вас є лише доступна частина файлу.


32

<zvrba> Дерева Ван Емде-Боаса

Я думаю, було б корисно знати, чому вони круті. Взагалі питання "чому" - це найважливіше задати;)

Моя відповідь полягає в тому, що вони дають вам словники O (log n) з клавішами {1..n}, незалежно від кількості клавіш. Подібно до того, як повторна половина дає вам O (log n), повторне sqrting дає вам O (log n), що і відбувається у дереві vEB.


Вони приємні з теоретичної точки зору. На практиці, однак, вивести з них конкурентоспроможність досить складно. Мені відомий папір змусив їх працювати добре до 32 бітових клавіш ( citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.2.7403 ), але підхід не може перевищувати 34-35 біт або тому і цього немає.
Мануель

Ще одна причина, чому вони круті - це те, що вони є ключовим складовим елементом для ряду алгоритмів, що не піддаються кешу.
Едвард КМЕТТ


29

Цікавий варіант хеш-столу називається зозуляним хешуванням . Він використовує декілька хеш-функцій замість лише 1 для вирішення хеш-зіткнень. Зіткнення вирішуються шляхом видалення старого об'єкта з місця, визначеного первинним хешем, і переміщення його до місця, визначеного альтернативною хеш-функцією. Cuckoo Hashing дозволяє більш ефективно використовувати простір пам’яті, оскільки ви можете збільшити коефіцієнт завантаження до 91%, лише за допомогою 3-х хеш-функцій і при цьому мати хороший час доступу.


5
Перевірте, чи стверджується, що хешування хепшотів стверджується швидше.
chmike

27

Мін-макс купа є варіацією купи , яка реалізує чергу роздвоєного пріоритету. Це досягається простою зміною властивості купи: Дерево, як кажуть, впорядковано min-max, якщо кожен елемент на парних (непарних) рівнях менший (більший), ніж усі діти та онуки. Рівні нумеруються починаючи з 1.

http://internet512.chonbuk.ac.kr/datastructure/heap/img/heap8.jpg


Хитрий у здійсненні. Навіть найкращі програмісти можуть помилитися.
finnw

26

Мені подобаються кешові Oblivid структури даних . Основна ідея полягає в тому, щоб викласти дерево в рекурсивно менші блоки, щоб кеші різного розміру скористалися перевагами блоків, зручних для них. Це призводить до ефективного використання кешування для всіх, починаючи від кешу L1 в оперативній пам'яті до великих фрагментів зчитування даних з диска, не знаючи особливості розмірів будь-якого з цих шарів кешування.


Цікава транскрипція з цього посилання: "Ключ - це макет ван Емде Боас, названий на честь структури даних про дерево Ван Емде Боас, задуманий у 1977 році Пітером ван Емде Боасом"
сергіол

23

Ліві схилені червоно-чорні дерева . Значно спрощена реалізація червоно-чорних дерев Робертом Седжевіком опублікована в 2008 році (~ половина рядків коду для реалізації). Якщо у вас коли-небудь виникали проблеми з обгортанням голови навколо реалізації Червоно-Чорного дерева, прочитайте про цей варіант.

Дуже схожий (якщо не ідентичний) на дерева Андерссона.



19

Бутстрапіруемие косі біноміальні купи по Джерті Стлтінг Бродал і Кріс Окасаки:

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

  • O(1)розмір, з'єднання , вставка, мінімум
  • O(log n) deleteMin

Зауважте, що об'єднання займає O(1)швидше, ніж O(log n)час, на відміну від більш відомих купи, які зазвичай охоплюються підручниками по структурі даних, наприклад, ліві купи . І на відміну від купи Фібоначчі , ці асимптотики є найгіршими, а не амортизованими, навіть якщо вони застосовуються наполегливо!

У Haskell є кілька реалізацій .

Їх спільно вивели Бродал і Окасакі, після того, як Бродал придумав імперативну купу з тими ж асимптотиками.


18
  • Kd-Trees , структура просторових даних, що використовується (серед інших) у режимі реального часу в режимі реального часу, має і зворотний бік: трикутники, що перетинаються, перетинаються між різними просторами, повинні бути вирізані. Зазвичай БВХ швидші, оскільки вони легші.
  • MX-CIF Quadtrees , зберігайте обмежувальні коробки замість довільних точкових наборів, поєднуючи звичайне квадри з бінарним деревом на краях квадратиків.
  • HAMT , ієрархічна хеш-карта з часом доступу, яка, як правило, перевищує хеш-карти O (1) через константи.
  • Інвертований індекс , досить відомий у колах пошукової системи, оскільки він використовується для швидкого пошуку документів, пов’язаних з різними пошуковими термінами.

Більшість із них, якщо не всі, зафіксовані в Словнику алгоритмів та структур даних NIST


18

Бальні дерева. Просто тому, що вони змушують людей хихикати.

Кульове дерево - це структура даних, яка індексує точки в метричному просторі. Ось стаття про їх побудову. Вони часто використовуються для пошуку найближчих сусідів до точки або прискорення k-засобів.


Вони також широко відомі як дерева "vantage point" або vp-дерева. en.wikipedia.org/wiki/Vp-tree
Едвард KMETT

17

Не насправді структура даних; більше способу оптимізації динамічно розподілених масивів, але буфери розривів, які використовуються в Emacs, є начебто крутими.


1
Я б точно вважав це структурою даних.
Крістофер Барбер

Для всіх, хто цікавиться, саме так реалізовані моделі документа (наприклад, PlainDocument), що підтримує текстові компоненти Swing; перед 1.2 я вважаю, що моделі документів були прямими масивами, що призводить до жахливих характеристик вставки для великих документів; як тільки вони переїхали до Гафер-Буферів, знову все було в порядку зі світом.
Ріяд Калла

16

Дерево Фенвік. Це структура даних, щоб зберігати підрахунок суми всіх елементів у векторі між двома заданими піддіадексами i та j. Тривіальне рішення, попередньо розраховуючи суму з початку, не дозволяє оновлювати елемент (вам потрібно виконати роботу O (n), щоб не відставати).

Дерева Fenwick дозволяють оновлювати та запитувати в O (журнал n), і як це працює, це дійсно круто і просто. Це дійсно добре пояснено в оригінальному папері Fenwick, що знаходиться у вільному доступі тут:

http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol24/issue3/spe884.pdf

Його батько, дерево RQM, також дуже класно: воно дозволяє зберігати інформацію про мінімальний елемент між двома індексами вектора, а також працює в O (log n) оновленнях і запитах. Мені подобається викладати спочатку RQM, а потім дерево Fenwick.


Боюся, це дублікат . Можливо, ви хочете додати до попередньої відповіді?
Франсуа Г

Також пов'язані дерева дерев сегментів, які корисні для виконання всіляких запитів діапазону.
dhruvbird


13

Вкладені набори приємні для представлення дерев у реляційних базах даних та запуску запитів на них. Наприклад, ActiveRecord (ORM за замовчуванням Ruby on Rails) поставляється з дуже простим вкладеним плагіном , що робить роботу з деревами дрібницею.


12

Це досить домен, але структура даних на пів-краю є досить акуратною. Він забезпечує спосіб переробки полігонових сіток (граней та ребер), що дуже корисно в комп'ютерній графіці та обчислювальній геометрії.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.