Які переваги дерев двійкового пошуку над хеш-таблицями?
Хеш-таблиці можуть шукати будь-який елемент за Theta (1) час, і так само легко додати елемент .... але я не впевнений, що переваги будуть навпаки.
Які переваги дерев двійкового пошуку над хеш-таблицями?
Хеш-таблиці можуть шукати будь-який елемент за Theta (1) час, і так само легко додати елемент .... але я не впевнений, що переваги будуть навпаки.
Відповіді:
Пам’ятайте, що двійкові пошукові дерева (на основі довідок) ефективні в пам’яті. Вони не залишають більше пам’яті, ніж потрібно.
Наприклад, якщо хеш-функція має діапазон R(h) = 0...100
, вам потрібно виділити масив зі 100 (покажчиків на) елементів, навіть якщо ви просто хешуєте 20 елементів. Якби ви використовували двійкове дерево пошуку для зберігання тієї самої інформації, ви виділяли б лише стільки місця, скільки вам потрібно, а також деякі метадані про посилання.
Однією з переваг, яку ніхто інший не зазначив, є те, що двійкове дерево пошуку дозволяє ефективно шукати діапазон.
Щоб проілюструвати свою ідею, я хочу зробити крайній випадок. Скажімо, ви хочете отримати всі елементи, чиї ключі від 0 до 5000. А насправді є лише один такий елемент та 10000 інших елементів, чиї ключі не входять у діапазон. BST може виконувати пошук діапазону досить ефективно, оскільки не шукає піддіаграму, на яку неможливо відповісти.
Хоча, як можна здійснювати пошук діапазону в хеш-таблиці? Вам або потрібно повторити кожен простір відра, який є O (n), або ви повинні шукати, чи існує кожен з 1,2,3,4 ... до 5000. (а що стосується клавіш від 0 до 5000 - це нескінченний набір? наприклад, ключі можуть бути десятковими знаками)
Однією «перевагою» двійкового дерева є те, що воно може пройти для перерахування всіх елементів у порядку. Це не є неможливим для таблиці Hash, але це не нормальна операція, одна конструкція в хешовану структуру.
Окрім усіх інших хороших коментарів:
Таблиці хешу, як правило, мають кращу поведінку кешу, що вимагає менше читання пам'яті порівняно з двійковим деревом. Для хеш-таблиці зазвичай проводите лише одне зчитування, перш ніж ви отримаєте доступ до посилання, що містить ваші дані. Двійкове дерево, якщо воно є збалансованим варіантом, вимагає чогось у порядку k * lg (n) пам'яті, яке читається для деякої постійної k.
З іншого боку, якщо противник знає вашу хеш-функцію, противник може змусити вашу хеш-таблицю робити зіткнення, що значно утрудняє її ефективність. Вирішення завдання полягає у виборі хеш-функції випадковим чином із сім’ї, але BST не має цього недоліку. Крім того, коли тиск на хеш-таблицю зростає занадто сильно, ви часто прагнете збільшити та перерозподілити хеш-таблицю, що може бути дорогою операцією. BST має тут більш просту поведінку і не має тенденції раптово виділяти багато даних і робити повторну операцію.
Дерева, як правило, є кінцевою середньою структурою даних. Вони можуть виступати у вигляді списків, легко розбиваються на паралельну роботу, мають швидке видалення, вставлення та пошук у порядку O (lg n) . Вони нічого особливо добре не роблять, але теж не мають надто поганої поведінки.
Нарешті, BST набагато простіше реалізувати на (чистих) функціональних мовах порівняно з хеш-таблицями, і вони не потребують впровадження деструктивних оновлень ( аргумент стійкості Паскаля вище).
BSTs are much easier to implement in (pure) functional languages compared to hash-tables
- справді? Я хочу зараз вивчити функціональну мову!
Основними перевагами бінарного дерева над хеш-таблицею є те, що бінарне дерево дає дві додаткові операції, які ви не можете (легко, швидко) виконати з хеш-таблицею
знайти елемент, найближчий до (не обов'язково рівний) деякому довільному значенню ключа (або найближчому вище / внизу)
повторіть вміст дерева в упорядкованому порядку
Два пов'язані між собою - двійкове дерево зберігає його вміст у відсортованому порядку, тому речі, які потребують цього упорядкованого порядку, легко зробити.
(Збалансоване) двійкове дерево пошуку також має перевагу в тому, що його асимптотична складність насправді є верхньою межею, тоді як "постійні" часи для хеш-таблиць є амортизованими часом: Якщо у вас є непридатна хеш-функція, ви можете закінчитися деградуванням до лінійного часу , а не постійний.
Хештел зайняв би більше місця при його першому створенні - у ньому будуть доступні слоти для елементів, які ще потрібно вставити (незалежно від того, чи вони коли-небудь вставлені), двійкове дерево пошуку буде настільки великим, як потрібно бути. Крім того, коли хеш-таблиці потрібно більше місця, розширення на іншу структуру може зайняти багато часу, але це може залежати від реалізації.
Двійкове дерево пошуку може бути реалізовано за допомогою стійкого інтерфейсу, коли нове дерево повертається, але старе дерево продовжує існувати. Ретельно реалізовані, старі та нові дерева ділять більшість своїх вузлів. Це неможливо зробити зі стандартною хеш-таблицею.
Бінарне дерево повільніше шукає та вставляє в нього, але має дуже приємну особливість обходу інфіксації, що по суті означає, що ви можете перебирати вузли дерева в упорядкованому порядку.
Ітерація через записи хеш-таблиці просто не має великого сенсу, оскільки всі вони розсіяні в пам'яті.
З тріщини інтерв'ю кодування, 6-е видання
Ми можемо реалізувати хеш-таблицю з збалансованим деревом бінарного пошуку (BST). Це дає нам час пошуку O (log n). Перевагою цього є потенційне використання менше місця, оскільки ми більше не виділяємо великий масив. Ми також можемо перебирати клавіші по порядку, що може бути корисним іноді.
BST також надають операції "findPredecessor" та "findSuccessor" (щоб знайти наступні найменші та наступні за величиною елементи) за час O (logn), що також може бути дуже зручним. Таблиця хешу не може забезпечити ефективність цього часу.
Якщо ви хочете отримати доступ до даних впорядкованому порядку, відсортований список повинен вестись паралельно хеш-таблиці. Хороший приклад - словник у .Net. (див. http://msdn.microsoft.com/en-us/library/3fcwy8h6.aspx ).
Це має побічний ефект не тільки уповільнення вставки, але і вимагає більшого обсягу пам'яті, ніж b-дерево.
Крім того, оскільки b-дерево сортується, легко знайти діапазон результатів або виконати об'єднання або злиття.
Це також залежить від використання, Hash дозволяє знайти точну відповідність. Якщо ви хочете запитувати діапазон, то BST - це вибір. Припустимо, у вас багато даних e1, e2, e3 ..... en.
За допомогою хеш-таблиці ви можете знаходити будь-який елемент за постійний час.
Якщо ви хочете знайти значення діапазону більше e41 і менше e8, BST може швидко це знайти.
Ключова річ - хеш-функція, яка використовується для уникнення зіткнення. Звичайно, ми не можемо повністю уникнути зіткнення, і в цьому випадку вдаємося до ланцюжків чи інших методів. Це робить витягнення більше не постійним часом у гірших випадках.
Після заповнення хеш-таблиця повинна збільшити розмір відра і скопіювати знову всі елементи. Це додаткова вартість, що не перевищує BST.
Таблиці хешу не корисні для індексації. Коли ви шукаєте діапазон, BST краще. Саме тому більшість індексів баз даних використовують дерева B + замість таблиць Hash
Двійкові дерева пошуку є хорошим вибором для реалізації словника, якщо ключі мають певний загальний порядок (ключі порівнянні), визначені на них, і ви хочете зберегти інформацію про замовлення.
Оскільки BST зберігає інформацію про замовлення, вона надає вам чотири додаткові операції динамічного набору, які неможливо виконати (ефективно) за допомогою хеш-таблиць. Ці операції:
Усі ці операції, як і кожна операція BST, мають складність у часі O (H). Крім того, всі збережені ключі залишаються відсортованими в BST, таким чином, ви можете отримати відсортовану послідовність клавіш, просто перебравши дерево в порядку.
Якщо підсумовувати, якщо все, що вам потрібно, це операції вставити, видалити та видалити, тоді хеш-таблиця є неперевершеною (більшу частину часу) у виконанні. Але якщо ви хочете виконати будь-які або всі перераховані вище операції, ви повинні використовувати BST, бажано BST, що самоврівноважує.
Основна перевага хеш-таблиці полягає в тому, що вона робить майже всі ops в ~ = O (1). І це дуже просто для розуміння та реалізації. Це дійсно ефективно вирішує багато "інтерв'ю". Тож якщо ви хочете зламати інтерв'ю з кодуванням, подружтесь із хеш-таблицею ;-)
Хешмап - це набір асоціативних масивів. Отже, ваш масив вхідних значень збирається у відра. У відкритій схемі адресації у вас є вказівник на відро, і кожен раз, коли ви додаєте нове значення у відро, ви дізнаєтесь, де у відрі є вільні пробіли. Існує кілька способів зробити це - ви починаєте на початку відра і збільшуєте вказівник кожен раз і перевіряєте, чи займає його. Це називається лінійним зондуванням. Потім ви можете здійснити двійковий пошук на зразок додавання, де ви подвоюєте різницю між початком відра та місцем, коли ви подвоюєте або зменшуєте подальше зменшення кожного разу, коли шукаєте вільний простір. Це називається квадратичним зондуванням. ГАРАЗД. Тепер проблеми обох цих методів полягають у тому, що якщо відро переливається у наступну адресу відра, то вам потрібно
ГАРАЗД. але якщо ви використовуєте зв'язаний список, не повинно бути такої проблеми, правда? Так, у пов'язаних списках у вас немає цієї проблеми. Зважаючи на те, що кожне відро починається із пов’язаного списку, і якщо у вас є 100 елементів у відрі, потрібно вимагати переходу цих 100 елементів, щоб дійти до кінця пов'язаного списку, отже List.add (Елемент Е) потребує часу, щоб -
Перевага реалізації пов'язаного списку полягає в тому, що вам не потрібна операція розподілу пам'яті та передача / копія O (N) усіх відроків, як у випадку реалізації відкритої адреси.
Таким чином, спосіб мінімізувати операцію O (N) - це перетворити реалізацію на Джерело пошуку бінарних файлів, де операціями пошуку є O (log (N)), і ви додаєте елемент у його положення на основі його значення. Додатковою особливістю BST є те, що він поставляється сортованим!
Двійкові дерева пошуку можуть бути швидшими при використанні клавіш рядка. Особливо, коли струни довгі.
Двійкові дерева пошуку, використовуючи порівняння для менших / більших, які швидкі для рядків (коли вони не рівні). Таким чином, BST може швидко відповісти, коли рядок не знайдено. Коли його буде знайдено, потрібно буде виконати лише одне повне порівняння.
У хеш-таблиці. Вам потрібно обчислити хеш рядка, і це означає, що вам потрібно пройти всі байти хоча б один раз, щоб обчислити хеш. Потім знову, коли знайдеться відповідна запис.