Вы находитесь на странице: 1из 13

Математические основы алгоритмов, осень 2022 г.

Лекция 5. Умножение булевых матриц: метод четырёх


русских. Двоичные деревья поиска, АВЛ-деревья.
B-деревья. Понятие о красно-чёрных деревьях∗
Александр Охотин

12 декабря 2022 г.

Содержание
1 Метод четырёх русских 1

2 Двоичные деревья поиска 3

3 АВЛ-деревья 5

4 B-деревья 9

1 Метод четырёх русских


Первый чисто комбинаторный метод умножения булевых матриц за время 𝑜(𝑛3 ) изобрели
Арлазаров, Диниц, Кронрод и Фараджев [1970], и в честь первооткрывателей он носит имя
метода четырёх русских (Method of Four Russians).
Пусть 𝐴 и 𝐵 — две булевы матрицы размера 𝑛 × 𝑛, цель — вычислить их произведе-
ние 𝐶 = 𝐴𝐵. Пусть 𝑘 — число, немного меньшее, чем log2 𝑛, и пусть 𝑛 делится на 𝑘; если
не делится, то 𝑛 можно немного увеличить, чтобы делилось. Каждая строка матрицы 𝐴
делится на 𝑛𝑘 векторов размера 1 × 𝑘, называемых кусочками. Кусочки в 𝑖-й строке обозна-
чаются через 𝐴𝑖,1 , . . . 𝐴𝑖, 𝑛𝑘 ∈ B1×𝑘 . Матрица 𝐵 разделяется на 𝑛𝑘 подматриц размера 𝑘 × 𝑛,
называемых полосами и обозначаемых через 𝐵1 , . . . , 𝐵 𝑛𝑘 ∈ B1×𝑘 . Тогда всякая 𝑖-я строка 𝐶,
обозначаемая через 𝐶𝑖 ∈ B1×𝑛 , представима в виде поразрядной дизъюнкции 𝑛𝑘 строк раз-
мера 1 × 𝑛, где каждая 𝑟-я строка получена умножением кусочка 𝐴𝑖,𝑟 на соответствующую
полосу 𝐵𝑟 , как показано на рис. 2.
𝑛
𝑘
⋁︁
𝐶𝑖 = 𝐴𝑖,𝑟 𝐵𝑟
𝑟=1

Действительно, по определению произведения матриц, 𝑐𝑖,𝑗 = 1 тогда и только тогда, когда


𝑎𝑖,ℓ = 1 и 𝑏ℓ,𝑗 = 1 для какого-то ℓ. Пусть 𝑟 — номер кусочка, в который попал элемент 𝑎𝑖,ℓ ,
и пусть 𝑡 — позиция внутри этого кусочка. Тогда в произведении 𝐴𝑖,𝑟 𝐵𝑟 будет вычислена

1
Рис. 1: Владимир Арлазаров (род. 1939), Ефим Диниц (род. 1949), Михаил Кронрод, Игорь
Фараджев (1939 или 1940–2020).

k
A B C

r Br k

Ai,r
i Ci
r

Рис. 2: Метод четырёх русских: произведение кусочка 𝐴𝑖,𝑟 размера 1 × 𝑘 на полосу 𝐵𝑟


размера 𝑘 × 𝑛, вносящее свой вклад в 𝑖-ю строку матрицы 𝐶.

конъюнкция 𝑎𝑖,ℓ ∧ 𝑏ℓ,𝑗 , и она попадёт в 𝑗-ю позицию строки 𝐶𝑖 .


Эта формула для вычисления произведения булевых матриц математически ничем не
отличается от вычисления по определению: следуя ей, будут вычислены те же самые 𝑛3
конъюнкций и (𝑛 − 1)𝑛2 дизъюнкций. Чисто программистские улучшения в ней есть: вы-
числять поразрядные дизъюнкции строк матрицы на компьютерах очень удобно, такие
однотипные векторные операции будут выполняться куда быстрее, чем если ковыряться в
отдельных битах.
Но главная идея метода четырёх русских в другом. Так как значение 𝑘 невелико, воз-
можных кусочков всего 2𝑘 , и одни и те же кусочки будут встречаться повторно. Поэтому
в ходе работы алгоритма одинаковые кусочки будут то и дело умножаться на одну и ту
же полосу. Вместо того, чтобы всякий раз умножать их заново, стоит заранее вычислить
произведения всех возможных кусочков размера 1 × 𝑘 со всеми полосами матрицы 𝐵. Для
каждого кусочка (𝑥1 , . . . , 𝑥𝑘 ) ∈ B𝑘 и для каждой полосы 𝐵𝑟 , алгоритм вычисляет следующее
произведение вектора на матрицу.

𝐷𝑟 [𝑥1 , . . . , 𝑥𝑘 ] = (𝑥1 , . . . , 𝑥𝑘 ) · 𝐵𝑟

Получившаяся строка размера 1 × 𝑛 сохраняется в памяти. Это произведение можно вы-


числить с помощью поразрядной дизъюнкции всех строк 𝐵𝑟 , соответствующих единичным
битам кусочка; такая операция эффективно реализуется на компьютерах.
Построив таблицу, алгоритм вычисляет всякую 𝑖-ю строку матрицы 𝐶 в виде следу-
ющей поразрядной дизъюнкции строк: каждое произведение 𝐴𝑖,𝑟 𝐵𝑟 берётся из таблицы,

Краткое содержание лекций, прочитанных студентам 1-го курса факульте-
та МКН СПбГУ в осеннем семестре 2022–2023 учебного года. Страница курса:
http://users.math-cs.spbu.ru/~okhotin/teaching/algorithms1_2022/.

2
проиндексированной 𝑘 битами кусочка 𝐴𝑖,𝑟 и номером полосы 𝑟.
𝑛
𝑘
⋁︁
𝐶𝑖 = 𝐷𝑟 [𝐴𝑖,𝑟 ],
𝑟=1

Осталось подсчитать общее число операций над битами, вычисляемых алгоритмами. На


этапе построении таблицы рассматриваются 2𝑘 возможных значений кусочков, матрица 𝐵
состоит из 𝑛𝑘 полосок, и всякое произведение требует 𝑘𝑛 битовых операций. Поэтому вы-
полняется 2𝑘 · 𝑛𝑘 · 𝑘𝑛 = 2𝑘 𝑛2 операций над битами. Далее, каждой из 𝑛 строк матрицы 𝐶
3
вычисляется поразрядной дизъюнкцией 𝑛𝑘 строк длины 𝑛 битов каждая — всего 𝑛𝑘 опера-
ций. Пусть 𝑘 = log2 𝑛 − log2 log2 𝑛. Тогда общее число операций подсчитывается так.

𝑛3 𝑛3 𝑛3 (︁ 𝑛3 )︁
2𝑘 𝑛2 + = + =𝑂
𝑘 log2 𝑛 log2 𝑛 − log2 log2 𝑛 log 𝑛

При реализации на современном компьютере алгоритм может хранить много битов в


машинном слове и использовать поэлементную дизъюнкцию машинных слов, чтобы произ-
вести много операций над битами за одну инструкцию процессора.
На практике метод четырёх русских следует использовать для матриц, начиная при-
мерно с 16 × 16 и заканчивая примерно 8192 × 8192. Для матриц большего размера более
эффективным оказывается метод Штрассена в кольце по модулю 𝑛 + 1.

2 Двоичные деревья поиска


2.1 Структуры данных для представления множеств
Структуры данных для представления множества различными способами, с разным вре-
менем выполнения операций (строго говоря, речь идёт о мультимножестве, поскольку
может быть несколько элементов с одинаковыми значениями). Элементы могут быть лю-
бого вида, на них определено отношение порядка “⩽”. Операции: найти элемент с данным
значением; найти наименьший (наибольший) элемент; найти элемент, предшествующий по
порядку данному (следующий за данным); вставить элемент; удалить элемент.

2.2 Двоичные деревья поиска


Двоичное дерево поиска (binary search tree, BST) — представление множества в виде дерева.
Вершина содержит одно значение и три указателя: на предка (если это корень, то там
NULL), на левое поддерево и на правое (если поддерева нет, то там NULL). При этом все
вершины в её левом поддереве содержат не меньшие значения, а все вершины в правом
поддереве — не бо́льшие.
Обход дерева: перечислить все элементы по порядку. Можно, например, рекурсией: спер-
ва обойти левое поддерево, потом вывести значение в данной вершине, потом обойти правое.
Но рекурсия не обязательна, достаточно помнить текущую вершину и откуда алгоритм в
неё пришёл.
Операции над деревом.

Поиск. Начиная с корня, сравнивать искомый элемент с текущим, и если они не равны,
следовать в нужное поддерево.

Найти наименьший (наибольший) элемент. Начиная с корня, переходить всё время


к левому (правому) потомку. Никаких сравнений не потребуется.

3
212 <4
4
x
212 >2
x x 2 6
2 <31
2
t1 t2
1 3 5 7

212

Рис. 3: Двоичное дерево поиска: (слева) условие для каждого поддерева; (справа) поиск
элемента 2 12 с последующей вставкой.

Найти следующий по порядку элемент. На входе — указатель на текущий элемент.


Если у него есть правое поддерево, то это — наименьший элемент правого поддерева.
Если же правого поддерева нет, то надо подниматься по дереву наверх, и как только
текущее дерево окажется левым поддеревом чего-то, следующий элемент найден.
Предыдущий элемент — симметрично.

Добавление элемента. Сперва найти место, где он должен был бы быть — поиском вниз
до листа. А далее сделать новый элемент левым или правым потомком этого листа, в
зависимости от значения.

Удаление элемента 𝑥 (дан указатель на него). Если у элемента 𝑥 нет потомков, то


его можно просто удалить. Если потомок один, то он занимает место 𝑥.
Если потомка два, то, как показано на рис. 4, находится элемент 𝑦, следующий по
порядку за 𝑥 (он в его правом поддереве). Он и занимает место удаляемого элемента.
Если у 𝑦 есть правый потомок, то тот в свою очередь занимает освободившееся место,
где был 𝑦.

Сложность каждой операции — не более чем высота дерева. Если дерево сбалансировано —
т.е., все пути примерно одинаковой длины — то все операции выполняются за логарифми-
ческое время.

x y

y z

Рис. 4: Двоичное дерево поиска: удаление вершины 𝑥, у которой есть оба потомка.

Как обеспечить сбалансированность дерева? Даже если оно сбалансировано изначаль-


но, то операции добавления и удаления элементов его разбалансируют. Поэтому необходи-
мо тем или иным образом усложнить структуру данных, ввести какие-то дополнительные
ограничения на неё, чтобы после выполнения каждой операции дерево автоматически ба-
лансировалось бы.

4
x 4 y 5

2 9 2 9

y z
1 3 5 10 1 3 7 10

z 6 8
7

6 8

Рис. 5: Удаление в двоичном дереве поиска: удаляется 𝑥 = 4.

Рис. 6: Георгий Адельсон-Вельский (1922–2014), Евгений Ландис (1921–1997).

Требование, чтобы все поддеревья содержали одинаковое число элементов, или же от-
личающееся не более чем на один — бессмысленно жёсткое, оно сделает балансировку тру-
доёмкой. Дереву нужно дышать! Нужно позволить ему быть местами немного разбаланси-
рованным, чтобы при добавлении элементов в малонаселённые места или при удалении из
густонаселённых мест ничего балансировать не приходилось бы. Есть несколько разновид-
ностей таких деревьев.

3 АВЛ-деревья
АВЛ-деревья (Адельсон-Вельский и Ландис [1962]; AVL trees): усложнённая разновидность
двоичных деревьев поиска.
«Почти сбалансированное» двоичное дерево, в котором высота поддеревьев-потомков
всякой вершины отличается не более чем на 1. Этого ограничения достаточно, чтобы дерево
имело логарифмическую высоту. «Дышат» за счёт трёх возможных разностей высоты.
Дополнительно в каждой вершине хранится высота её поддерева — достаточно даже не
высоты, а разности между высотой правого и левого поддеревьев.

3.1 Высота АВЛ-дерева. Числа Фибоначчи

Лемма 1. АВЛ-дерево высоты ℎ содержит не менее чем 𝐹ℎ+3 − 1 вершин, где 𝐹𝑛 — 𝑛-е
число Фибоначчи.

5
Леонардо Пизанский (Фибоначчи) (ок. 1175–ок. 1250): последовательность 𝐹 (𝑛). В первый
месяц была одна пара кроликов. Каждый месяц каждая пара возраста не менее двух месяцев
производит на свет новую пару. Кролики не умирают. Сколько будет пар на 𝑛-м месяце?
𝐹1 = 1, 𝐹2 = 1, 𝐹𝑛 = 𝐹𝑛−1 + 𝐹𝑛−2 — то есть, все, кто были в прошлом месяце, есть и сейчас,
и все, кто были два месяца назад, производят потомство. В качестве начальных условий
удобнее положить 𝐹0 = 0, 𝐹1 = 1.

Доказательство леммы 1. Индукция по ℎ.


Базовый случай: ℎ = 0, это одинокая вершина, и 𝐹0+3 − 1 = 1.
Переход: если дерево имеет высоту ℎ, то один из его потомков имеет высоту ℎ − 1, и
потому содержит не менее чем 𝐹ℎ+2 − 1 вершину, а другой — высоту не менее чем ℎ − 2, и,
стало быть, не менее чем 𝐹ℎ+1 − 1 вершину. Всего, с учётом корня, 1 + 𝐹ℎ+2 − 1 + 𝐹ℎ+1 − 1 =
𝐹ℎ+3 − 1 вершин.

А сколько это?

√ √
√1 (𝜙𝑛 1+ 5 1− 5
Утверждение 1. 𝐹𝑛 = 5
− 𝜓 𝑛 ), где 𝜙 = 2 (золотое сечение) и 𝜓 = 2 .

Отсюда 𝐹𝑛 = √1 𝜙𝑛
5
− 𝑜(1). Приближённые значения: 𝜙 ≈ 1.62 и 𝜓 ≈ −0.62.

Лемма 2. Высота АВЛ-дерева с 𝑛 вершинами не превосходит log𝜙 𝑛.

Поэтому поиск в АВЛ-дереве занимает логарифмическое время.


При вставке элемент вставляется, как в обычное дерево поиска. Однако при этом де-
рево может разбалансироваться, то есть у некоторых вершин высота поддеревьев может
начать различаться на 2 (но не более чем на 2). Поэтому после вставки всегда проводится
исправление после вставки, чтобы дерево осталось правильным АВЛ-деревом.
Элемент удаляется, как из обычного дерева поиска, с последующим исправлением после
удаления.

3.2 Балансировка АВЛ-дерева


Пусть разность высоты поддеревьев-братьев — не более чем 2, и все такие разбалансиро-
ванные вершины находятся на одном пути из корня. Тогда находится самое нижнее раз-
балансированное поддерево (где разность равна двум), и разбалансировка проталкивается
вверх по дереву. Если высота этого поддерева — ℎ, то у него два поддерева высоты ℎ − 1
и ℎ − 3. У поддерева высоты ℎ − 1 — свои поддеревья: одно высоты ℎ − 2, другое высоты
ℎ − 3 или ℎ − 2. Четыре случая, в зависимости от того, через каких потомков (правых или
левых) идёт путь в поддерево высоты ℎ − 2: левый-левый, левый-правый, правый-левый и
правый-правый.
Основная элементарная операция: вращение (или поворот). Пусть в дереве поиска есть
две соседние вершины 𝑥, 𝑦, где 𝑥 — левый потомок 𝑦, у 𝑥 поддеревья 𝑡1 и 𝑡2 , а правый
потомок 𝑦 — 𝑡3 ; схематично: (𝑡1 𝑥𝑡2 )𝑦𝑡3 . Тогда эту структуру внутри дерева можно изменить,
поставив 𝑥 наверх и получив 𝑡1 𝑥(𝑡2 𝑦𝑡3 ) — дерево останется правильным деревом поиска. Это
— вращение между 𝑥 и 𝑦. Точно так же можно вращать и в обратном направлении.
Ниже приведены подробности применения вращения для балансировки дерева после
операций вставки и удаления.

6
y x

x y
t3 t1

t1 t2 t2 t3

Рис. 7: Вращение между вершинами 𝑥 и 𝑦 в AVL-деревьях.


h h+1
y y x h
h–1
h–1 h–2 h h–2 h–1
x x y
t3 t3
t1'
h–2 h–2 h–1 h–2 h–2 h–2

t1 t2 t2 t2 t3
t1'

Рис. 8: Исправление после вставки, случай «левый-левый», одно вращение: (слева) исходное
дерево; (посередине) дерево после вставки в 𝑡1 ; (справа) дерево после вращения.

3.3 Исправление после вставки


Все разбалансированные вершины находятся на пути из корня во вставленный лист. Рас-
сматривается самая нижняя из разбалансированных вершин, 𝑦. Пусть ℎ — высота вершины
𝑦 до операции вставки, пусть высота её потомков до вставки — ℎ − 1 и ℎ − 2, и пусть вставка
производится в потомке, имеющем высоту ℎ − 1; на рис. 8(левом) этот потомок обозначает-
ся через 𝑥. Потомки 𝑥 имеют одну и ту же высоту ℎ − 2 (действительно, имей они разную
высоту, после вставки в более низкое поддерево разбалансировки вообще не возникло бы, а
после вставки в более высокое поддерево разбалансировка возникла бы уже в вершине 𝑥).
После вставки вершина 𝑥 получит высоту ℎ, а вершина 𝑦 — высоту ℎ+1, как изображено
на рис. 8(среднем). На этом рисунке показан случай, когда вставка производится в левом
потомке левого потомка вершины 𝑦 — её «левом-левом» потомке. Бросается в глаза, что
поддерево 𝑡′1 выше двух других, однако прицеплено снизу — и хочется переставить эти под-
деревья так, чтобы 𝑡′1 оказалось прицеплено на уровень выше. Для исправления достаточно
одного вращения между 𝑥 и 𝑦: на рис. 8(правом) представлено получившееся исправленное
поддерево, которое опять имеет высоту ℎ. Стало быть, разбалансировка исправлена одним
этим вращением.
Случай вставки в правом-правом потомке самой нижней разбалансированной вершины
симметричен.
Пусть вершина вставлена в левом-правом потомке, как показано на рис. 9(сверху). Пусть
𝑧 — этот потомок. Тогда после вставки 𝑧 имеет высоту ℎ − 1, а высота каждого из его
потомков — или ℎ−2, или ℎ−3. Этот случай изображён на рис. 9(снизу слева). Тогда сперва
производится вращение между 𝑥 и 𝑧, получается дерево, как на рис. 9(снизу посередине),
а потом делается ещё одно вращение между 𝑧 и 𝑦. Итоговое поддерево, приведённое на
рис. 9(снизу справа), сбалансировано и имеет высоту ℎ.
Случай вставки в правом-левом потомке симметричен.
Во всех случаях высота исправленной вершины становится такой же, как была до встав-
ки, и потому разбалансированность не распространяется выше по дереву.
Из этого также следует, что когда вставка приводит к разбалансировке какой-то вер-
шины, высота всего дерева в таком случае не увеличивается. Действительно, высота всего

7
h y h+1 y

h–1 h–2 h h–2


x x
t3 t3
h–2 h–2 h–2 h–1

t1 t2 t1
t2'

h+1 h+1
y y z h

h h–2 h h–2 h–1 h–1


x z x y
h–1 t3 h–1 t3 h–2
h–2 или h–2 h–2
h–2 h–2 z h–2 x или h–2
h–2 h–3 или
или или h–2
h–3 h–3 или h–3
t1 h–3 t2'' h–3 t1 t2' t2'' t3
t2' t2'' t1 t2'

Рис. 9: Исправление после вставки: случай «левый-правый». Сверху: слева исходное дерево
до вставки, справа — после вставки. Снизу: слева структура левого-правого потомка 𝑧,
посередине — после первого вращения, справа — после второго вращения.

дерева увеличивается тогда, когда дерево уже переполнено — то есть когда у корня высоты
ℎ оба поддерева имели высоту ℎ − 1, и высота одного из этих поддеревьев увеличивается.

3.4 Исправление после удаления


Вершина удаляется из АВЛ-дерева, как из обычного дерева поиска. После удаления рас-
сматривается самая нижняя разбалансированная вершина: если её высота — ℎ, то одно из
её поддеревьев имеет высоту ℎ − 1, а другое (то, где проведено удаление) — ℎ − 3. В зависи-
мости от структуры поддерева высоты ℎ − 1, есть три случая исправления. Если поддерево
состоит из двух поддеревьев высоты ℎ − 2, как на рис. 10, то исправление ограничивается
одним вращением. Высота дерева остаётся равной ℎ, поэтому сверху ничего не меняется.
h y h y h
x
h–1 h–2 h–1 h–3
h–2 h–1
x x y

t3 t3' t1
h–2 h–2 h–2 h–2 h–2 h–3

t2 t3'
t1 t2 t1 t2

Рис. 10: Исправление после удаления, случай «слева одинаковые», одно вращение: (слева)
исходное дерево; (посередине) дерево после удаления из 𝑡3 ; (справа) дерево после вращения.

Пусть поддерево высоты ℎ − 1 — левое, и пусть его левое поддерево выше правого. То-
гда высота двух поддеревьев левого поддерева — ℎ − 2 и ℎ − 3, как изображено на рис. 11.
После одного вращения условия АВЛ-дерева выполнены, однако высота исходной верши-
ны уменьшилась с ℎ до ℎ − 1. В этом случае условия АВЛ-дерева могут нарушиться для
каких-то вершин, расположенных выше. Поэтому алгоритм продолжает исправление после
удаления, поднявшись на уровень выше. В крайнем случае так придётся дойти до самого
корня, уменьшение высоты которого уже не нарушит никаких условий.

8
h y h y h–1
x
h–1 h–2 h–1 h–3
h–2 h–2
x x y

t3 t3' t1
h–2 h–3 h–2 h–3 h–3 h–3

t2 t2 t2 t3'
t1 t1

Рис. 11: Исправление после удаления, случай «слева больше левое», одно вращение: (слева)
исходное дерево; (посередине) дерево после удаления из 𝑡3 ; (справа) дерево после вращения,
его высота уменьшилась.

Наконец, если поддерево высоты ℎ − 1 — левое, и его левое поддерево ниже правого, то
высота правого-левого поддерева — ℎ − 2, и оно состоит из двух поддеревьев, каждое из
которых имеет высоту ℎ − 3 или ℎ − 4. Тогда, как показано на рис. 12, четыре указанных
поддерева выравниваются на один уровень, что можно сделать двумя операциями враще-
ния. В итоге высота исходной вершине уменьшится до ℎ − 1 и исправление после удаления
продолжится на уровень выше.
h y h y h–1
z
h–1 h–2 h–1 h–3 h–2 h–2
x x x y

t4 t4'
h–2 h–2 h–3 h–3
h–3 z h–3 z h–3 или или h–3
h–4 h–4
t1 h–3 h–3
или или
t1 h–3 h–3
или или
t1 t2 t3 t4'
h–4 h–4 h–4 h–4
t2 t3 t2 t3

Рис. 12: Исправление после удаления в АВЛ-дереве, случай «слева больше правое», два вра-
щения: (слева) исходное дерево; (посередине) дерево после удаления из 𝑡4 ; (справа) дерево
после двух вращений, его высота уменьшилась.

4 B-деревья
Двоичные деревья поиска рассчитаны на хранение в оперативной памяти компьютера, поз-
воляющей за одну операцию обратиться не более чем к нескольким байтам. Каждая вер-
шина дерева может быть обработана за несколько таких операций, и такое использование
оперативной памяти оптимально.
Для хранения деревьев во внешней, медленной памяти (такой, как жёсткий диск) струк-
туру данных необходимо адаптировать. Главная особенность внешней памяти в том, что за
одну операцию читается или записывается блок данных размером в несколько килобайт
— например, сектор на жёстком диске. Поэтому для доступа к одной вершине двоичного
дерева пришлось бы работать с целым блоком, и поиск в дереве с 𝑛 вершинами потребовал
бы порядка log2 𝑛 операций с блоками. Это неоптимально.
Предложенные Байером и Маккрайтом [1972] B-деревья — это адаптация деревьев по-
иска для хранения во внешней памяти. Главная мысль — использовать вершины большой
степени — с тем, чтобы каждая вершина занимала один блок, а высота дерева уменьшилась
бы. Например, если все вершины имеют степень 1000, то высота дерева с миллиардом вер-

9
Рис. 13: Рудольф Байер (род. 1939), Эдвард Маккрайт.

шин будет равна всего лишь трём (а не тридцати, как у двоичного дерева), и поиск нужного
листа потребует прочитать лишь 4 блока.
Пусть вершины в двоичном дереве — это «2-вершины», поскольку у каждой из них 2
потомка и 1 значение, по которому эти потомки разделяются. У 𝑚-вершины — 𝑚 потомков
(деревья 𝑡1 , . . . , 𝑡𝑚 — возможно, пустые), и в ней находится 𝑚 − 1 значение: 𝑥1 , . . . , 𝑥𝑚−1 ,
где 𝑥1 ⩽ . . . ⩽ 𝑥𝑚−1 . Все значения в каждом поддереве 𝑡𝑖 больше или равны 𝑥𝑖 и меньше
или равны 𝑥𝑖+1 , как показано на рис. 14.
В B-дереве могут одновременно содержаться вершины различных степеней: выбирается
некоторое число 𝑘 ⩾ 2, после чего корень может иметь степень от 0 до 2𝑘, а все остальные
вершины — любые степени от 𝑘 до 2𝑘. При этом дерево сбалансировано: длины всех путей
равны.

x1 ... xi –1 xi ... xm –1

x1 xi –1 xi xm –1

t1 ti tm

Рис. 14: 𝑚-вершина в B-дереве

При поиске элемента 𝑥 в B-дереве, на каждом шаге рассматривается некоторая 𝑚-


вершина, в которой размещены значения 𝑥1 , . . . , 𝑥𝑚−1 . В этом массиве запускается дво-
ичный поиск элемента 𝑥. Если 𝑥 находится в массиве, то поиск в дереве на этом завершён,
а если нет, то двоичный поиск указывает на поддерево 𝑡𝑖 , для которого верно 𝑥𝑖−1 < 𝑥 < 𝑥𝑖 ,
и потому элемент 𝑥 может находиться только в нём. Поиск продолжается в поддереве 𝑡𝑖 .

4.1 Вставка в B-деревo


В АВЛ-дереве вставка и удаление начинается с поиска данного элемента, после чего ис-
правляется возможная разбалансировка. В худшем случае придётся пройти путь от корня
к листу, а потом обратно от листа к корню.
В B-дереве вставка и удаление делаются иначе: исправление потенциальной разбалан-
сировки начинается уже на этапе поиска удаляемого элемента в дереве.
Пусть вставка или удаление производятся в листе, и это 𝑚-вершина. Тогда, если этот
лист заполнен не до конца (𝑚 < 2𝑘), то в нём найдётся место для дополнительного значения,
а если он заполнен не минимально (𝑚 > 𝑘), то из него можно удалить любое из его значений.

10
При вставке, спускаясь вниз по дереву, нужно разделять каждую очередную встречен-
ную «полную» 2𝑘-вершину на две 𝑘-вершины — за счёт её сестёр или родительницы. При
этом одно лишнее значение выталкивается на один уровень выше, как на рис. 15. Поскольку
на уровне выше не может быть 2𝑘-вершины (ведь алгоритм только что оттуда спустился),
в ней есть место, в которое можно вытолкнуть лишнее значение. В итоге найденный лист
тоже окажется степени не более чем 2𝑘 − 1 — то есть, в нём будет не более чем 2𝑘 − 1 пу-
стых указателей на несуществующих потомков, и между ними не более чем 2𝑘 −2 значений;
следовательно, в этом листе найдётся, куда вставить новое значение.

xyz x z

t1 t2 t3 t4 t1 t2 t3 t4

Рис. 15: Разделение вершины при вставке в B-дереве, для 𝑘 = 2: (слева) переполненная
4-вершина; (справа) выталкивание переполнения наверх.

Что делать, если при вставке окажется, что степень корня — 2𝑘? Здесь важно, что сте-
пень корня не ограничена снизу. Тогда корень точно так же разделяется на две 𝑘-вершины,
лишнее значение так же выталкивается на уровень выше, где появляется новый корень
степени 2, с одним значением. Высота дерева увеличивается только в этом случае.

4.2 Удаление в B-дереве


При удалении, спускаясь вниз, нужно точно так же увеличивать каждую встреченную 𝑘-
вершину за счёт её сестёр. Для всякой встреченной 𝑘-вершины уже обеспечено, что её
родительница — не менее чем (𝑘 + 1)-вершина. Значит, у текущей 𝑘-вершины есть не менее
𝑘 сестёр.

• Если одна из соседних сестёр — не менее чем (𝑘 + 1)-вершина, то недостающее подде-


рево перегоняется из неё, как показано на рис. 16.

y z

x z x y

... ...

t t

Рис. 16: Заимствование поддерева у соседней сестры при удалении в B-дереве: (слева) мало-
имущая 𝑘-вершина, выделенная красным; (справа) поддерево заимствовано у сестры спра-
ва.

• Если же соседняя сестра — 𝑘-вершина, то она объединяется с текущей 𝑘-вершиной в


одну 2𝑘-вершину — это в точности обратная операция к разделению вершины, изоб-

11
ражённому на рис. 15. При этом у родительницы станет на одно поддерево меньше,
но она это переживёт, поскольку она не 𝑘-вершина.

Когда же наконец находится вершина, содержащая удаляемое значение, если это лист, то
значение просто удаляется (он же степени хотя бы 𝑘 + 1!). Если же нужно удалить элемент,
находящийся во внутренней вершине, то запоминается указатель на эту вершину, после
чего делается следующее.

• Если в предшествующем значению поддереве, в его корне есть хотя бы 𝑘 + 1 значение,


то в поддереве находится значение-предшественник, и процедура стирания продолжа-
ется уже для него (а само оно замещает стираемый элемент).

• Аналогично в следующем поддереве.

• Если же у соседних поддеревьев по 𝑘 значений, то они объединяются в одно с 2𝑘


значениями.

Корень нужно рассмотреть особо. Поскольку его степень не ограничена снизу, сам по
себе он не нуждается в исправлении. Однако если корень — 2-вершина, а оба его потомка —
𝑘-вершины, то исправление этих 𝑘-вершин приведёт к полному опустошению корня. В этом
случае все три вершины объединяются в одну 2𝑘-вершину, а высота дерева уменьшается на
единицу.

4.3 Понятие о красно-чёрных деревьях


B-дерево для 𝑘 = 2 называется 2-3-4-деревом, и такое дерево удобно хранить в оперативной
памяти. Его высота незначительно меньше, чем у АВЛ-дерева однако оно требует более
одного сравнения на вершину и большого объёма кода для своей реализации.
Красно-чёрное дерево — это представление 2-3-4-дерева в виде двоичного дерева, в ко-
тором каждая вершина 2-3-4-дерева представлена в виде одной или нескольких связанных
между собою двоичных вершин, каждая из которых покрашена в один и двух цветов, крас-
ный или чёрный, причём верхняя из этих связанных вершин всегда чёрная. Преобразова-
ние показано на рис. 17. Каждая 2-вершина остаётся собой и считается чёрной. Каждая
3-вершина разбивается на две двоичных: чёрную вершину — корень поддерева, и красную
вершину — правого или левого потомка чёрной; к этой паре вершин, так же, как и к исход-
ной 3-вершине, присоединяются три поддерева. Наконец, каждая 4-вершина разбивается на
три двоичных — чёрный корень поддерева и её два потомка — красные вершины, к которым
присоединены четыре поддерева.
На красно-чёрное дерево налагаются следующие ограничения. Во-первых, две красных
вершины не могут быть соединены ребром — и благодаря этому каждое красно-чёрное де-
рево состоит из поддеревьев, изображённых на рис. 17(снизу). Во-вторых, от B-деревьев
унаследован вариант свойства равенства длин всех путей — в случае красно-чёрных дере-
вьев требуется, чтобы количество чёрных вершин на каждом пути из корня к листу было
одинаковым. Благодаря этим двум условиям, красно-чёрные деревья взаимно соответству-
ют 2-3-4-деревьям с точностью до двух вариантов представления каждой 3-вершины.
Использование красно-чёрных деревьев вместо 2-3-4-деревьев позволяет заменить слож-
ные операции для B-деревьев несколькими более простыми операциями над красно-
чёрными деревьями. Поэтому красно-чёрные деревья существенно удобнее программиро-
вать, чем 2-3-4-деревья. Они всё равно сложнее в программировании, чем АВЛ-деревья,
однако на практике они оказываются в небольшое константное число раз быстрее. Поэто-
му, например, std::set реализован именно с помощью красно-чёрных деревьев.

12
x x y xyz

t1 t2 t1 t2 t3 t1 t2 t3 t4

х х y y

y x x z

t1 t2

t1 t2 t3 t1 t2 t3 t1 t2 t3 t4

Рис. 17: Преобразование вершин 2-3-4-деревьев (сверху) во фрагменты красно-чёрных де-


ревьев (снизу)

Список литературы
[1962] Г. М. Адельсон-Вельский, Е. М. Ландис, “Один алгоритм организации информации”,
Доклады Академии наук СССР, 146:2 (1962), 263–266.

[1970] В. Л. Арлазаров, Е. А. Диниц, М. А. Кронрод, И. А. Фараджев, “Об экономном


построении транзитивного замыкания ориентированного графа”, Доклады Академии
наук СССР, 194:3 (1970), 487–488.

[1972] R. Bayer, E. M. McCreight, “Organization and maintenance of large ordered indexes”,


Acta Informatica, 1:3 (1972), 173–189.

[1978] L. J. Guibas, R. Sedgewick, “A dichromatic framework for balanced trees”, FOCS 1978,
8–21.

13

Вам также может понравиться