Хеш-таблиці проти бінарних дерев


30

Під час реалізації словника ("Я хочу шукати дані клієнтів за їх ідентифікаторами клієнтів") типовими структурами даних є хеш-таблиці та двійкові дерева пошуку. Я знаю, наприклад, що бібліотека STL C ++ реалізує словники (вони називають їх картами), використовуючи (врівноважені) двійкові дерева пошуку, а .NET Framework використовує хеш-таблиці під кришкою.

Які переваги та недоліки цих структур даних? Чи є якийсь інший варіант, розумний у певних ситуаціях?

Зауважте, що мене не особливо цікавлять випадки, коли ключі мають сильну основу структури, скажімо, вони є цілими числами від 1 до n або щось подібне.


1
Я змучу вас, але ви не можете просто сказати "цілі числа між 1 і n", оскільки в такому випадку масив випередить всі інші структури даних :-). "Струни" здаються справедливими і охоплюють більшість ситуацій.
jmad

@jmad сказав, що він не зацікавлений у цій справі.
Джо

@Joe Я подумав, що це зрозуміло, я врахував це. У всякому разі, це не привід навести найгірший можливий приклад ключа.
jmad

1
Насправді .NET має як словники, реалізовані за допомогою дерев, так і словники, реалізовані за допомогою хеш-таблиць (так само і C ++ з початку 2011 року).
sepp2k

Відповіді:


26

n

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

O(lg(n))log2(n)

2nO(1)

O(1)

  • O(n)
  • O(1)

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

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

k1k2h(k1)=h(k2)

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

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

Коли ключі - це рядки (або цілі числа), трие може бути іншим варіантом. Трие - це дерево, але індексоване інакше, ніж дерево пошуку: ви пишете ключ у двійковій формі, а ліворуч - на 0 і вправо на 1. Вартість доступу, таким чином, пропорційна довжині ключа. Спроби можна стиснути, щоб видалити проміжні вузли; це відоме як патріція трие або радіксне дерево . Дерева Radix можуть перевершувати збалансовані дерева, особливо коли багато клавіш мають спільний префікс.


2
Чи не мають BST також місцезростання даних?
svick

@svick Вони можуть, а можуть і ні, залежно від того, як розподіляються вузли. Збільшення масиву дерева може допомогти без шкоди для виконання часу (вартість більша і складніший код).
Жил "ТАК - перестань бути злим"

2
На BST легко дістати елементи "в порядку", для хеш-таблиці це не йдеться.
vonbrand

Окрім випадків безпеки, чому це важливо, якщо хеш-таблиці мають найгірший час, якщо їх середній регістр кращий, ніж у двійкових дерев? Я гадаю, що зручність / корисність користувачів має приблизно лінійний зв’язок із тим, скільки часу дерево потребує до завершення, тому очікуване (середнє) значення повинно бути усім важливим.
Кельмікра

@ Kyth'Py1k Що ви маєте на увазі під «деревом до кінця»? Суть хеш-таблиць - це отримати доступ до одного значення за раз, а не до всього дерева, інакше список або масив працюватимуть краще. Навіть у ситуаціях, де значення має середнє значення (що не завжди буває так, наприклад, коли у вас є обмеження в режимі реального часу), це середнє значення для запитів, які виконуються в тій чи іншій ситуації, які часто не є рівномірними для таблиці - наприклад, упереджено до певного префікса.
Жил "ТАК - перестань бути злим"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.