Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курсач по алгоритмизации.doc
Скачиваний:
11
Добавлен:
06.05.2019
Размер:
1.33 Mб
Скачать

1.6 Операции над красно-черными деревьями

1.6.1 Повороты

Операции над деревом поиска Tree_Insert и Tree_Delete, будучи применены к красно-черному дереву с n ключами, выполняются за время O(lg(n)). Поскольку они изменяют дерево, в результате их работы могут нарушаться красно-черные свойства. Для восстановления этих свойств мы должны изменить цвета некоторых узлов дерева, а также структуру его указателей.

Рис. 8. Операции поворота в бинарном дереве поиска.

Изменения в структуре указателей будут выполняться при помощи поворотов (rotations), которые представляют собой локальные операции в дереве поиска, сохраняющие свойство бинарного дерева поиска. На рис. 6 показаны два типа поворотов - левый и правый (здесь , и - произвольные поддеревья). При выполнении левого поворота в узле x предполагается, что его правый дочерний узел y не является листом nil[T]. Левый поворот выполняется "вокруг" связи между x и y, делая y новым корнем поддерева, левым дочерним узлом которого становится x, а бывший левый потомок узла y - правым потомком x.

В псевдокоде процедуры LEFT_ROTATE предполагается, что , а родитель корневого узла - nil[Т].

Left_Rotate(T, x)

1 > Устанавливаем у.

2 > Левое поддерево у становится

> правым поддеревом х

3 if

4 then

5 > Перенос родителя х в у

6 if

7 then

8 else if

9 then

10 else

11 > x - левый дочерний y

12

Рис. 9. Пример выполнения процедуры LEFT_ROTATE.

Код процедуры Right_Rotate симметричен коду Left_Rotate. Обе эти процедуры выполняются за время O(1). При повороте изменяются только указатели, все остальные поля сохраняют свое значение [3].

1.6.2 Вставка

Вставка узла в красно-черное дерево с n узлами может быть выполнена за время О(lg(n)). Для того чтобы вставка сохраняла красно-черные свойства дерева, после нее вызывается вспомогательная процедура RB_INSERT_FIXUP, которая перекрашивает узлы и выполняет повороты. Вызов RB_Insert(T, z) вставляет в красно-черное дерево Т узел z с заполненным полем key:

RB Insert(T, z)

1

2

3 while

4 do

5 if

6 then

7 else

8

9 if

10 then

11 else if

12 then

13 else

14

15

16

17 RB_INSERT_FIXUP(T, z)

Поскольку красный цвет z может вызвать нарушение одного из красно-черных свойств, в строке 17 вызывается вспомогательная процедура RB_Insert_Fixup(T, z), предназначение которой - восстановить красно-черные свойства дерева:

RB_Insert_Fixup(T, z)

1 while

2 do if

3 then

4 if

5 then > Случай 1

6 > Случай 1

7 > Случай 1

8 > Случай 1

9 else if

10 then z 4- p[z] > Случай 2

11 Left_Rotate(T, z) > Случай 2

12 > Случай 3

13 > Случай 3

14 > Случай 3

15 else (то же, что и в "then", с заменой left на right и наоборот)

16

Для того чтобы понять, как работает процедура RB_INSERT_FIXUP, разобьем рассмотрение кода на три основные части. Сначала определим, какие из красно-черных свойств нарушаются при вставке узла z и окраске его в красный цвет. Затем рассмотрим предназначение цикла while в строках 1-15. После этого изучим каждый из трех случаев, которые встречаются в этом цикле, и посмотрим, каким образом достигается цель в каждом случае. На рис. 8 показан пример выполнения процедуры RB_INSERT_FIXUP.

Какие из красно-черных свойств могут быть нарушены перед вызовом RB_InSERT_Fixup? Свойство 1 определенно выполняется (как и свойство 3), так как оба дочерних узла вставляемого узла являются ограничителями nil[T]. Свойство 5, согласно которому для каждого узла все пути от него до листьев, являющихся потомками данного узла, содержат одно и то же количество черных узлов, также остается в силе, поскольку узел z замещает (черный) ограничитель, будучи при этом красным и имея черные дочерние узлы. Таким образом, может нарушаться только свойство 2, которое требует, чтобы корень красно-черного дерева был черным, и свойство 4, согласно которому красный узел не может иметь красного потомка. Оба нарушения возможны в силу того, что узел z после вставки окрашивается в красный цвет. Свойство 2 оказывается нарушенным, если узел z становится корнем, а свойство 4 - если родительский по отношению к z узел является красным. На рис. 8а показано нарушение свойства 4 после вставки узла z.

Цикл while в строках 1-15 сохраняет следующий инвариант, состоящий из трех частей.

В начале каждой итерации цикла:

а) узел z красный;

б) если р[z] - корень дерева, то р[z] — черный узел;

в) если имеется нарушение красно-черных свойств, то это нарушение только одно - либо нарушение свойства 2, либо свойства 4. Если нарушено свойство 2, то это вызвано тем, что корнем дерева является красный узел z; если нарушено свойство 4, то в этом случае красными являются узлы z и p[z].

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

Поскольку мы сосредотачиваем свое рассмотрение только на узле z и узлах, находящихся в дереве вблизи него, полезно знать, что узел z - красный (часть а). Часть б используется для того, чтобы показать, что узел к которому мы обращаемся в строках 2, 3, 7, 8, 13 и 14, существует.

Инициализация. Перед выполнением первой итерации цикла имеется красно-черное дерево без каких-либо нарушений красно-черных свойств, к которому мы добавляем красный узел z. Покажем, что все части инварианта цикла выполняются к моменту вызова процедуры RB_Insert_Fixup.

а) В момент вызова процедуры RB_Insert_Fixup узел z — вставленный в дерево красный узел.

б) Если р[z] - корневой узел дерева, то он является черным и не изменяется до вызова процедуры RB_lNSERT_Fixup.

в) Мы уже убедились в том, что красно-черные свойства 1, 3 и 5 сохраняются к моменту вызова процедуры RB_Insert_Fixup.

Если нарушается свойство 2, то красный корень должен быть добавленным в дерево узлом z, который при этом является единственным внутренним узлом дерева. Поскольку и родитель, и оба потомка z являются ограничителями, свойство 4 не нарушается. Таким образом, нарушение свойства 2 - единственное нарушение красно-черных свойств во всем дереве.

Если же нарушено свойство 4, то поскольку дочерние по отношению к z узлы являются черными ограничителями, а до вставки z никаких нарушений красно-черных свойств в дереве не было, нарушение заключается в том, что и z, и р[z] - красные. Кроме этого, других нарушений красно-черных свойств не имеется.

Завершение. Как видно из кода, цикл завершает свою работу, когда р[z] становится черным (если z - корневой узел, то р[z] представляет собой черный ограничитель nil[Т]). Таким образом, свойство 4 при завершении цикла не нарушается. В соответствии с инвариантом цикла, единственным нарушением красно-черных свойств может быть нарушение свойства 2. В строке 16 это свойство восстанавливается, так что по завершении работы процедуры RB_Insert_Fixup все красно-черные свойства дерева выполняются.

Сохранение. При работе цикла while следует рассмотреть шесть разных случаев, однако три из них симметричны другим трем; разница лишь в том, является ли родитель р[z] левым или правым дочерним узлом по отношению к своему родителю р[р[z]], что и выясняется в строке 2 (мы привели код только для ситуации, когда р[z] является левым потомком). Узел р[р[z]] существует, поскольку, в соответствии с частью б) инварианта цикла, если р[z] - корень дерева, то он черный. Поскольку цикл начинает работу, только если р[z] - красный, то р[z] не может быть корнем. Следовательно, р[р[z]] существует. Случай 1 отличается от случаев 2 и 3 цветом "брата" родительского по отношению к z узла, т.е. "дяди" узла z. После выполнения строки 3 указатель у указывает на дядю узла z - узел right[р[р[z]]], и в строке 4 выясняется его цвет. Если у - красный, выполняется код для случая 1; в противном случае выполняется код для случаев 2 и 3. В любом случае, узел р[р[z]] - черный, поскольку узел р[z] - красный, а свойство 4 нарушается только между z np[z].

Рис. 10. Работа процедуры RB_INSERT_FIXUP

Случай 1: узел у красный.

На рис. 11 показана ситуация, возникающая в случае 1 (строки 5-8), когда и p[z], и у - красные узлы. Поскольку p[p[z]] - черный, мы можем исправить ситуацию, покрасив и p[z], и у в черный цвет (после чего цвет красного родителя узла z становится черным, и нарушение между z и его родителем исчезает), а р[р[z]] - в красный цвет, для того чтобы выполнялось свойство 5. После этого мы повторяем цикл while с узлом р[р[z]] в качестве нового узла z. Указатель z перемещается, таким образом, на два уровня вверх.

На рис. 11а z - правый дочерний узел, а на рис. 11б - левый. Как видите, предпринимаемые в обоих случаях одни и те же действия приводят к одинаковому результату. Все поддеревья ( и ) имеют черные корни и одинаковое значение черной высоты. После перекраски свойство 5 сохраняется: все нисходящие пути от узла к листьям содержат одинаковое число черных узлов. После выполнения итерации новым узлом z становится узел р[р[z]] и нарушение свойства 4 может быть только между новым узлом z и его родителем (который также может оказаться красным).

Теперь покажем, что в случае 1 инвариант цикла сохраняется. Обозначим через z узел z в текущей итерации, а через z'=р[р[z]] - узел z, проверяемый в строке 1 при следующей итерации.

а) Поскольку в данной итерации цвет узла р[р[z]] становится красным, в начале следующей итерации узел z' - красный.

б) Узел р[z'] в текущей итерации - р[р[р[z]]], и цвет данного узла в пределах данной итерации не изменяется. Если это корневой узел, то его цвет до начала данной итерации был черным и остается таковым в начале следующей итерации.

в) Мы уже доказали, что в случае 1 свойство 5 сохраняется; кроме того, понятно, что при выполнении итерации не возникает нарушение свойств 1 или 3.

Рис. 11. Случай 1 процедуры RB_lNSERT_FlXUP

Если узел z' в начале очередной итерации является корнем, то код, соответствующий случаю 1, корректирует единственное нарушение свойства 4. Поскольку узел z' - красный и корневой, единственным нарушенным становится свойство 2, причем это нарушение связано с узлом z'.

Если узел z' в начале следующей итерации корнем не является, то код, соответствующий случаю 1, не вызывает нарушения свойства 2. Этот код корректирует единственное нарушение свойства 4, имеющееся перед выполнением итерации. Коррекция выражается в том, что узел z' становится красным, в то время как цвет узла р[z'] не изменяется. Если узел р[z'] был черным, то свойство 4 не нарушается; если же этот узел был красным, то окрашивание узла z' в красный цвет приводит к нарушению свойства 4 между узлами z' и р[z'].

Случай 2: узел у черный и z — правый потомок.

Случай 3: узел у черный и z — левый потомок.

В случаях 2 и 3 цвет узла у, являющегося "дядей" узла z, черный. Эти два случая отличаются друг от друга тем, что z является левым или правым дочерним узлом по отношению к родительскому. Строки 10-11 псевдокода соответствуют случаю 2, который показан на рис. 12 вместе со случаем 3. В случае 2 узел z является правым потомком своего родительского узла. Используем левый поворот для преобразования сложившейся ситуации в случай 3 (строки 12-14), когда z является левым потомком. Поскольку и z, и р[z] - красные узлы, поворот не влияет ни на черную высоту узлов, ни на выполнение свойства 5. Когда мы приходим к случаю 3 (либо непосредственно, либо поворотом из случая 2), узел у имеет черный цвет (поскольку иначе мы бы получили случай 1). Кроме того, обязательно существует узел р[р[z]], так как мы доказали, что этот узел существовал при выполнение строк 2 и 3, а также что после перемещения узла z на один узел вверх в строке 10 с последующим опусканием в строке 11 узел р[р[z]] остается неизменным. В случае 3 мы выполняем ряд изменений цвета и правых поворотов, которые сохраняют свойство 5. После этого, так как у нас нет двух идущих подряд красных узлов, работа процедуры завершается. Больше тело цикла while не выполняется, так как узел р[z] теперь черный.

Теперь покажем, что случаи 2 и 3 сохраняют инвариант цикла.

а) В случае 2 выполняется присвоение, после которого z указывает на красный узел р[z]. Никаких других изменений z или его цвета в случаях 2 и 3 не выполняется.

б) В случае 3 узел р[z] делается черным, так что если р[z] в начале следующей итерации является корнем, то этот корень - черный.

в) Как и в случае 1, в случае 2 и 3 свойства 1, 3 и 5 сохраняются. Поскольку узел z в случаях 2 и 3 не является корнем, нарушение свойства 2 невозможно. Случаи 2 и 3 не могут приводить к нарушению свойства 2, поскольку при повороте в случае 3 красный узел становится дочерним по отношению к черному.

Таким образом, случаи 2 и 3 приводят к коррекции нарушения свойства 4, при этом не внося никаких новых нарушений красно-черных свойств.

Показав, что при любой итерации инвариант цикла сохраняется, мы тем самым показали, что процедура RB_Insert_Fixup корректно восстанавливает красно-черные свойства дерева [3].

Рис. 12. Случаи 2 и 3 процедуры RB_Insert_Fixup

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]