інтерфейс Map ┌───┘ │ └───┐ інтерфейс │ клас SortedMap │ Hashtable │ клас │ HashMap інтерфейс │ NavigableMap │ │ клас │ LinkedHashMap клас TreeMap
Втілюючи інтерфейси NavigableMap і SortedMap, TreeMap отримує додатковий функціонал, якого немає в HashMap. Але платою за це продуктивність. Час доступу до елементів TreeMap найтриваліший. Якщо не потрібно зберігати дані в упорядкованому вигляді, краще використати HashMap або LinkedHashMap.
Клас LinkedHashMap дозволяє зберігати дані у порядку додавання. Спільні й відмінні риси цих трьох класів подано такою таблицею.
HashMap | LinkedHashMap | TreeMap | |
---|---|---|---|
Порядок зберігання даних. | Випадковий |
У порядку додавання |
У порядку зростання |
Час доступу до елемента | O(1) | O(1) | O(log(n)) |
Імплементовані інтерфейси | Map | Map | NavigableMap, SortedMap, Map |
Основа втілення (структури даних) | Відра (buckets) | Відра (buckets) | Червоно-чорне дерево (Red-Black Tree) |
Використання ключа null | Можна | Можна |
Лише при використанні порівнювача, що дозволяє null |
Потоко- безпечність | Ні | Ні | Ні |
У цій таблиці використано такі поняття.
Відра (англійською buckets) — групи, на які поділяють елементи структури даних і які потім упорядковують окремо.
Червоно-чорне дерево (англійською red-black tree) — двійкове (бінарне) дерево пошуку, кожний вузол (вершина) якого має додаткове поле, що містить колір — червоний або чорний — і що має такі властивості:
Щодо терміну двійкове дерево пошуку нагадаємо таке.
Двійкове дерево — структура даних у вигляді дерева орієнтованого графа, в якому з кожної вершини (вузла) виходить не більше двох дуг (орієнтованих ребер).
Зазвичай такі графи подають рисунком, на якому корінь — вершину, яка не є кінцем жодної дуги — зображують вгорі, а вектори, що відображають дуги, спрямовано вниз-ліворуч або вниз-праворуч. Вершини, у які спрямовані ці дуги, називають відповідно лівим і правим нащадками вершини, з якої виходять ці дуги, а двійкові дерева, що є частинами початкового двійкового дерева з коренями у цих нащадках — лівим і правим піддеревами.
Зазвичай вузол двійкового дерева подають записом з такими полями:
У подальшому описі червоно-чорного дерева використано матеріали сторінки https://algorithmtutor.com/Data-Structures/Tree/Red-Black-Trees/.
При використанні такого дерева для зберігання пар ключ/значення:
На малюнку нижче подано зображення червоно-чорного дерева, використаного для упорядкування пар з ключами 1, 6, 8, 11, 13, 15, 17, 22, 25, 27 і запозиченого зі сторінки https://en.wikipedia.org/wiki/Red%E2%80%93black_tree.
Чорна висона вузла — кількість чорних вузлів на шдяху від даного вузла (не рахуючи його) до вузла, обидва нащадки якого NULL (рахуючи цей вузол).
Чорна висона червоно-чорного дерева — чорна висота його кореня.
Висота червоно-чорного дерева має порядок O(log n), де n — кількість вершин. Доведемо це.
Розглянемо випадок, коли усі вузли дерева чорні. Позначимо чорну довжину дерева через k. Маємо:
Додавання червоного вузла у дерево збільшує його висоту. Отже, червоно-чорне дерево чорної висоти k має щонайменше 2k – 1 вузлів. Червоно-чорне дерево має максимальну висоту, коли колір вузла на його найдовшому шляху змінюється при переході до наступного вузла. У цьому випадку чорна висота дерева дорівнює h/2, де h — фактична висота дерева — кількість усіх вершин, незалежно віл їхнього кольору, на шляху від кореня до вузла, обидва нащадки якого NULL. Маємо:
Серед операцій: пошук, попередник, наступник, вставлення, видалення — дві останні можуть порушити перелічені вище 5 властивостей. У цьому випадку потрібно перебудувати дерево одним з 3 способів, які мають такі назви:
x y / \ / \ a y → x c / \ / \ b c a b
y x / \ / \ x c → a y / \ / \ a b b c
Операція вставлення. Щоб вставити вузол K в червоно-чорне дерево T, потрібно зробити таке.
Розглянемо всі випадки вставлення, щоб пересвідчитися у можливості виправлення графу для задоволення усіх 5 властивостей червоно-чорного дерева. Використаємо такі позначення для вузлів безпосередньо після вставлення і перед виправленням:
P — батьківський вузол;
U — вузол дядька;
G — вузол дідуся.
У поданих нижче схемах (у тому числі і для операції вставлення) не відображено продовження вниз і вгору, вузол U може бути праворуч від вузла P.
G G / \ / \ U P → U P \ \ K K
Якщо G є коренем T, не потрібно змінювати колір G, бо це порушить властивість 2.
P — червоний, U — чорний (у тому числі NULL), P — праворуч від G, K — праворуч від P. У цьому випадку потрібно спочатку виконати обертання ліворуч в точці G, що зробить G братом K. Далі — змінити колір G на червоний, а колір P — на чорний.
G P P / \ / \ / \ U P → G K → G K \ / / K U U
P — червоний, U — чорний (у тому числі NULL), P — праворуч від G, K — ліворуч від P. У цьому випадку потрібно спочатку виконати поворот праворуч у точці P.
G G / \ / \ / \ → / \ U P U K / \ K P
А далі діяти так, як у попередньому випадку, із заміною K на P і навпаки.
P — червоний, U — чорний (у тому числі NULL), P — ліворуч від G, K — ліворуч від P. Цей випадок симетричний випадку 4, тому достатньо викоконати відповідно симетричні дії.
P — червоний, U — чорний (у тому числі NULL), P — ліворуч від G, K — праворуч від P. Цей випадок симетричний випадку 5, тому достатньо викоконати відповідно симетричні дії.
Операція видалення. При видаленні вузла з двома нелистовими нащадками у звичайному двійковому дереві пошуку шукають або найбільший елемент у його лівому піддереві, або найменший елемент у його правому піддереві і переміщують значення знайденого вузла у вузол, що планували видаляюти спочатку. Копіювання значення з одного вузла в інший не порушує властивостей червоно-чорного дерева, бо структуру дерева та кольори вузлів не змінено. Потім видаляють вузол, з якого копіювали значення. Цей вузол не може мати два дочірніх нелистових вузла, бо інакше він не буде найбільшим/найменшим елементом серед тих, які не перевищують / не менші від того, який видаляють. Отже, випадок видалення вузла, що має два нелистові нащадки, зводиться до випадку видалення вузла, що має не більше одного нелистового нащадка. Надалі обмежимося розглядом лише такого випадку видалення вузла А, щоб пересвідчитися у можливості виправлення графу для задоволення усіх 5 властивостей червоно-чорного дерева.
A є червоним вузлом. У цьому випадку видалення A не порушує жодної властивості червоно-чорного дерева.
A є чорним вузлом і має червоного нащадка. У цьому випадку замінюють A його червоним нащадком і змінюють колір нащадка на чорний.
Примітка. Якщо A є чорним вузлом і має чорного нащадка, просте видалення чорного вузла A порушить властивість 5. На поданих нижче схемах перетворення фіолетовим кольором позначено вузли, колір яких може бути як червоним, так і чорним. Розглянуто лише випадки 3-6, коли А є лівим нащадком. Інші 4 випадки, коли А є правим нащадком, симетричні розглянутим.
А є чорним вузлом, має чорного предка В й червоного брата D. У цьому випадку змінюють кольори брата і предка, а потім виконують поворот ліворуч на предку B.
B D / \ / \ / \ → / \ A D B E / \ / \ C E A C
Таким чином задачу видалення вузла А зведено до одного з наступних трьох випадків 4-7.
B B / \ / \ / \ → / \ A D A D / \ / \ C E C E
B B / \ / \ / \ → / \ A D A D / \ / \ C E C E
Інакше після зміни кольору D на червоний потрібно поєднати вузли А і В у так званий подвійний чорний вузол і призначити цей подвійний чорний вузол для видалення. Але перед видаленням потрібно розгрупувати цей подвійний чорний вузол і видалити лише вузол А.
А є чорним вузлом і має чорного брата, лівий нащадок якого червоний, а правий — чорний. Потрібно змінити кольори брата D та його лівого нащадка С, виконати поворот праворуч у вузлі С й отримати дерево, розглянуте у наступному пункті 7.
B B / \ / \ / \ → / \ A D A C / \ \ C E D \ E
B D / \ / \ / \ → / \ A D B E / \ / \ C E A C
┌──────G──────┐ C M / \ / \ / \ / \ A E K O \ / \ / \ / \ B D F H L N P ↓ ┌──────G──────┐ AC M / \ / \ / \ / \ B E K O / \ / \ / \ D F H L N P ↓ ┌──────G──────┐ C M / \ / \ / \ / \ B E K O / \ / \ / \ D F H L N P