За старих часів у класі структур даних ми дізналися, як працюють дерева AVL . Я б мав це в одному з моїх занять, але викладач сказав: "Ви ніколи цього не використовуєте", а натомість навчив нас 2-3 дерева та b * дерева. Це були дні, коли пам’ять була тісною і процеси були окремо нарізані. Ви не використовували декес, коли спільно пов'язаний список працював би так само добре.
Сьогодні список пропусків набагато частіше зустрічається, тому що більше доступної пам’яті та одночасності є проблемою (вам взагалі не потрібно дуже сильно фіксувати, коли ви виступаєте як автор у списку пропусків - порівняно з усім із деревом AVL).
Відверто кажучи, зараз це моя улюблена структура даних в тому, що я щось легко можу пояснити, як вона працює внизу і де її буде вигідно або невигідно використовувати.
Вам не потрібно буде писати його з нуля (якщо ви не отримаєте це як питання інтерв'ю - але тоді ви настільки ж шанси отримати реалізацію дерева AVL).
Ви будете потребувати , щоб зрозуміти , чому ви хочете , щоб вибрати ConcurrentSkipListMap
в Java , а не HashMap
чи , TreeMap
або будь-який з інших реалізацій карти.
Щоб зрозуміти, як це працює, потрібно зрозуміти, як працює бінарне дерево. Зачекайте, дозвольте мені змінити це. Вам потрібно зрозуміти, як працює збалансоване бінарне дерево. Не врівноважуючи бінарне дерево, ви не отримаєте реальної переваги при його пошуку.
Скажімо, у нас є це дерево:
І ми вставляємо в нього «8». Тепер у нас є:
І це не врівноважено. Отже, ми робимо магію врівноваження її за допомогою певної реалізації ...
І у вас знову є врівноважене дерево. Але це було багато магії, я махнув рукою.
Дозволяє взяти пропускний список.
Це буває ідеалізованим. Мало хто є, але це показує врівноважену двійкову природу дерева, яку ідеал пропускного списку наближає.
Тепер ми хочемо вставити туди 6. Це вставляється так, як пов'язаний список. Однак ми починаємо зверху і спускаємося вниз. Верхні бали до 5. Чи 6> 5? Так. Гаразд, вершина вказує до кінця зараз, тому ми йдемо вниз по стеку (ми на 5). Наступний - 7. 6> 7? Ні. Таким чином ми опускаємось на рівень і ми знаходимося на базовому рівні, тому вставляємо 6 праворуч від 5.
Ми гортаємо монету - голови ми будуємо, хвости залишаємося. Хвости. Більше нічого не потрібно робити.
Давайте вставимо 8 зараз. 8> 5? так. 8> 7? Так. І тепер ми знову на нижньому рівні після слідування стрілок і стека навколо, і ми тестуємо 8> 11? Ні. Отже, вставляємо 8 праворуч від 7.
Ми гортаємо монету - голови ми будуємо, хвости залишаємося. Хвости. Більше нічого не потрібно робити.
У збалансованому дереві ми б працювали над тим, щоб дерево зараз не було врівноваженим. Але це не дерево - його пропускний список. Ми наближаємо збалансоване дерево.
Тепер давайте вставити 10. Я уникаю всіх порівнянь.
Ми гортаємо монету - голови ми будуємо, хвости залишаємося. Голови! І переверніть його знову, голови! Переверніть знову, добре, там є хвости. Два рівні над базовим пов'язаним списком.
Перевага тут полягає в тому, що зараз, якщо ми збираємося вставити 12, ми можемо пропустити від 5 до 10, не роблячи всіх інших порівнянь. Ми можемо пропустити їх зі списком пропусків. І нам не потрібно турбуватися про збалансоване дерево - імовірнісний характер укладання робить це для нас.
Чому це так корисно? Тому що, вставляючи 10, я можу це зробити, заблокувавши написання на 5 та 7 та 8 покажчиках, а не на всю структуру. І хоча я це роблю, читачі все ще можуть переглядати список пропусків, не маючи непослідовного стану. Для одночасного використання його швидше не потрібно блокувати. Для ітерації над нижнім шаром його швидше, ніж дерево (радощі алгоритмів BFS та DFS для навігації по дереву - про них не потрібно турбуватися).
Чи зіткнешся ти з цим? Напевно, ви побачите його у використанні місцями. І тоді ви будете знати , чому автор вибрав цю реалізацію , а не TreeMap
чи HashMap
для структури.
Значна частина цього була запозичена з моєї публікації в блозі: Пропустити список