П’ять незмінних величин, що обмежують висоту
Червоно-чорне дерево — це бінарне пошукове дерево, де кожен вузол має бути розфарбований червоним або чорним, і дотримуються чотири додаткові правила — поряд із звичайним упорядкуванням BST:
1. Кожен вузол є червоним або чорним. 2. Корінь – чорний. 3. Будь-яке листове дерево (NIL / null) вважається чорним. 4. Червоний вузол не має червоного дитини (немає двох червоних у рядку на жодному шляху). 5. Для будь-якого заданого вузла кожен шлях від нього до будь-яких його листових вузлів-NULL проходить через однакову кількість чорних вузлів — "чорну висоту" цього вузла.
Жоден з цих правил не згадує висоту безпосередньо, що є ключем: замість відстеження та ребалансування за точним числом висоти (як у AVL-дереві), червоно-чорне дерево забезпечує локальний колірний обмежений, який дешево перевіряється та дешево виправляється після одного вставлення або видалення, і це обмеження випадково передбачає глобальне обмеження висоти.
1. Every node is red or black. 2. The root is black. 3. Every leaf (NIL / null) is considered black. 4. A red node never has a red child (no two reds in a row on any path). 5. Every path from a given node to any of its descendant NIL leaves passes through the same number of black nodes — the node's "black-height".
Почему высота остается O(log n)
Правило 4 гласит, что в строке нельзя иметь двух красных узлов подряд, поэтому на любом пути от корня к листу не более чем каждые другие узлы будут красными. Это означает, что общая длина пути не превышает дважды его черного уровня. Правило 5 утверждает, что все пути от корня имеют один и тот же черный уровень bh. Поддерево с черным уровнем bh содержит как минимум 2^bh − 1 внутренних узлов (чистая индукция: удвоение черного уровня как минимум удваивает количество узлов, поскольку оба ребенка черного узла имеют черный уровень bh−1 или bh). Объединяя это:
n >= 2^bh - 1 (n = количество внутренних узлов) => bh <= log2(n + 1) => высота <= 2 * bh <= 2 * log2(n + 1) Таким образом, независимо от того, в каком порядке вставляются или удаляются ключи, самая длинная возможная длина пути от корня до листа никогда не превышает примерно дважды наименьшую, и обе они равны O(log n). Поиск, вставка и удаление все проходят по не более чем одному пути от корня до листа, поэтому все три случаются в худшем случае O(log n) — та же асимптотическая гарантия, что и для идеально сбалансированного дерева, при меньшей стоимости перебалансировки.
n >= 2^bh - 1 (n = number of internal nodes) => bh <= log2(n + 1) => height <= 2 * bh <= 2 * log2(n + 1)
Повороти та перефарбовування при вставці
Новий вузол завжди фарбується в червоний колір спочатку (це ніколи не порушує правила чорного рівня, оскільки він не додає жодних чорних вузлів до будь-якого шляху) і потім виконується процедура виправлення, яка йде вгору, виправляючи будь-які порушення червоного-червоного, які вона спричинила. Виправлення має дві дуже різні особистості залежно від кольору родича нового вузла (брата батька:)
родич є ЧЕРВОНИМ: перефарбуйте батька та родича в чорний колір, перефарбуйте бабусю в червоний колір, потім продовжуйте виправлення, починаючи з бабусі (може розгортатися до кореня) родич є ЧОРНИМ (або відсутній): 1-2 дерева обертання (випади LL, LR, RL або RR) плюс перефарбування, яке виправляє порушення локально в O(1) обертаннях — не потрібно додаткового розгортання live demo · inserting keys and watching rotations keep it balanced● LIVE Випадок лише перефарбування може, в принципі, поширитися на всі шляхи до кореня, але кожен випадок обертання негайно припиняє виправлення, що пояснює, чому вставка все ще коштує O(1) середніх обертів, незважаючи на те, що перефарбування ланцюга є O(log n) у найгіршому випадку.
uncle is RED: recolour parent and uncle black, grandparent red, then continue fixing up from the grandparent (may cascade to the root) uncle is BLACK (or missing): 1-2 tree rotations (LL, LR, RL or RR case) plus a recolour, which fixes the violation locally in O(1) rotations — no further cascading is needed
Видалення вузла, що виявляється чорним, може знизити чорний рівень одного піддерева нижче його братів, створюючи "дворазову чорну" дефіцит, який потрібно вирішити шляхом більш тривалого аналізу випадку, ніж вставку — класична реалізація має чотири окремі випадки кольору братів, кожна з яких має свій рецепт обертання та перефарбування. Це єдина причина, чому реалізації червоного-чорного дерева відомі своєю складністю у розробці з нуля, і чому більшість виробничого коду звертається до добре протестованої реалізації бібліотеки замість написання її з нуля.
Deleting a node that turns out to be black can drop the black-height of one subtree below its siblings, creating a "double-black" deficiency that has to be pushed upward and resolved through a longer case analysis than insertion needs — the classic implementation has four distinct sibling-colour cases, each with its own rotation and recolour recipe. It is the single reason red-black tree implementations are notoriously fiddly to get right from scratch, and why most production code reaches for a well-tested library implementation rather than writing one from memory.
Где це фактично використовується
C++'s std::map і std::set є, в кожному основному стандартному забезпеченні реалізації, біло-червоними деревами. TreeMap і TreeSet Java також такі. Linux kernel's Completely Fair Scheduler підтримує виконувані процеси у біло-червоному дереві, відсортованому за віртуальним часом виконання, тому обидва вибирати наступний процес для запуску та повторно вставляти процес після його запуску є операціями O(log n) на дереві, яке залишається збалансованим під непередбачуваним, високочастотним потоком вставлення та видалень — саме робоче навантаження, для якого оптимізовані біло-червоні дерева.
Frequently asked questions
Чому не використовувати просто бінарне дерево пошуку?
Просте BST не має гарантії балансу. Якщо вставляти ключі в відсортованому порядку, воно деградує до зв’язаного списку, з пошуком, вставкою та видаленням O(n) замість O(log n). Червоно-чорне дерево витрачає трохи додаткових обчислень на кожну вставку та видалення, щоб запобігти цьому деградації, гарантуючи, що висота ніколи не перевищує приблизно 2·log2(n+1) незалежно від порядку вставки.
Чи є червоно-чорні дерева ідеально збалансованими?
Ні, і це навмисно. Ідеально збалансоване дерево, таке як AVL, підтримує два піддерева на кожному вузлі в межах 1 за висотою, що забезпечує швидші пошуки, але вимагає більше обертань при вставці та видаленні. Червоно-чорне дерево гарантує лише те, що найдовший шлях від кореня до листового вузла не перевищує удвічі найкоротший, що є більш м’яким обмеженням, але потребує менше операцій ребалансування, роблячи його швидшим для навантажень з частими записами.
Де використовуються червоно-чорні дерева насправді?
Стандартна бібліотека C++ реалізує std::map та std::set як червоно-чорні дерева, так само як TreeMap та TreeSet у Java. Повністю справедливий планувальник ядра Linux використовує червоно-чорне дерево, відсортоване за віртуальним часом виконання, щоб вибрати наступний процес для запуску з O(log n), а також багато інтервальних дерев баз даних та мов програмування будуються на основі червоно-чорного дерева.
Спробуйте наживо
Усе, що вище, працює прямо у вашому браузері — відкрийте Red-Black Trees і змінюйте параметри під час роботи. Нічого не встановлюється, нічого не завантажується на сервер, уся модель живе в одній вкладці.
▶ Відкрити симуляцію Red-Black Trees