7.4 Удаление вершины
Как и остальные базовые операции над красно-черными деревьями с п узлами, удаление узла выполняется за время O(lоg2n). Удаление оказывается несколько более сложной задачей, чем вставка.
RB_Delete(T,z)
1 if left[z]=nil[T] или right[z]=nil[T]
2 then yz
3 else yTree_Successor(z)
4 if left[y]nil[T]
5 then xleft[y]
6 else xright[y]
7 p[x] p[y]
8 if p[y]=nil[T]
9 then root[T] x
10 else if y=left[p[y]]
11 then left[p[y]] x
12 else right[p[y]] x
13 if y z
14 then key[z] key[y]
15 Копируем сопутствующие данныеy в z
16 if color[y]=black
17 then RB_Delete_Fixup(T,x)
18 return y
Процедура RB_DELETE представляет собой немного измененную процедуру Tree_Delete раздела 6.3. После удаления узла в ней вызывается вспомогательная процедура RB_Delete_Fixup, которая изменяет цвета и выполняет повороты для восстановления красно-черных свойств дерева.
Имеется три различия между процедурами Tree_Delete и RB_Delete. Во-первых, все ссылки на nil в Tree_Delete заменены в RB_Delete ссылками на ограничитель nil [Т]. Во-вторых, удалена проверка в строке 7 процедуры Tree_Delete (равно ли х nil), и присвоение р [х] р [у] выполняется в процедуре RB_Delete безусловно. Таким образом, если х является ограничителем nil [T], то его указатель на родителя указывает на родителя извлеченного из дерева узла у. В-третьих, в строках 16-17 процедуры RB_DELETE в случае, если узел у - черный, выполняется вызов вспомогательной процедуры RB_Delete_Fixup. Если узел у - красный, красно-черные свойства при извлечении у из дерева сохраняются в силу следующих причин:
никакая черная высота в дереве не изменяется;
никакие красные узлы не становятся соседними;
• так как у не может быть корнем в силу своего цвета, корень остается черным.
Узел х, который передается в качестве параметра во вспомогательную процедуру RB_DELETE_FlXUP, является одним из двух узлов: либо это узел, который был единственным потомком у перед извлечением последнего из дерева (если у у был дочерний узел, не являющийся ограничителем), либо, если у узла у нет дочерних узлов, х является ограничителем nil [T]. В последнем случае безусловное присвоение в строке 7 гарантирует, что родительским по отношению к х узлом становится узел, который ранее был родителем у, независимо от того, является ли х внутренним узлом с реальным ключом или ограничителем nil [T].Теперь мы можем посмотреть, как вспомогательная процедура RB_lNSERT_ FlXUP справляется со своей задачей восстановления красно-черных свойств дерева поиска.
Если извлекаемый из дерева в процедуре RB_Delete узел у черный, то могут возникнуть три проблемы. Во-первых, если у был корнем, а теперь корнем стал красный потомок у, нарушается свойство 2. Во-вторых, если и ее, и р[у] (который теперь является также р [х]) были красными, то нарушается свойство 4, и, в-третьих, удаление у приводит к тому, что все пути, проходившие через у, теперь имеют на один черный узел меньше. Таким образом, для всех предков у оказывается нарушенным свойство 5. Мы можем исправить ситуацию, утверждая, что узел х - "сверхчерный", т.е. при рассмотрении любого пути, проходящего через х, добавлять дополнительную 1 к количеству черных узлов. При такой интерпретации свойство 5 остается выполняющимся. При извлечении черного узла у мы передаем его "черноту" его потомку. Проблема заключается в том, что теперь узел х не является ни черным, ни красным, что нарушает свойство 1. Вместо этого узел х окрашен либо "дважды черным", либо "красно-черным" цветом, что дает при подсчете черных узлов на пути, содержащем х, вклад, равный, соответственно, 2 или 1. Атрибут color узла х при этом остается равен либо BLACK (если узел дважды черный), либо RED (если узел красно-черный). Другими словами, цвет узла не соответствует его атрибуту color.
Процедура RB_Delete_Fixup восстанавливает свойства 1, 2 и4. Цель циклаwhile в строках 1-22 состоит в том, чтобы переместить дополнительную черноту вверх по дереву до тех пор, пока не выполнится одно из перечисленных условий.
х указывает на красно-черный узел — в этом случае мы просто делаем узелх "единожды черным" в строке 23.
х указывает на корень — в этом случае мы просто убираем излишнюю черноту.
Можно выполнить некоторые повороты и перекраску, после которых двойная чернота будет устранена.
Внутри цикла while x всегда указывает на дважды черную вершину, не являющуюся корнем. В строке 2 мы определяем, является лих левым или правым дочерним узлом своего родителяр[х]. Далее приведен подробный код для ситуации, когдах - левый потомок. Для правого потомка код аналогичен и симметричен, и скрыт за описанием в строке 22. Указательwуказывает на второго потомка родителя х. Поскольку узелх дважды черный, узелw не может бытьnil [T] — в противном случае количество черных узлов на пути от р [х] к (единожды черному) листу было бы меньше, чем количество черных узлов на пути от р[х] к х.
Четыре разных возможных случая показаны на рис. 7.6. Перед тем как приступить к детальному рассмотрению каждого случая, убедимся, что в каждом из случаев преобразования сохраняется свойство 5. Ключевая идея заключается в необходимости убедиться, что при преобразованиях в каждом случае количество черных узлов (включая дополнительную черноту в х) на пути от корня включительно до каждого, из поддеревьев остается неизменным. Такимобразом, если свойство4выполнялось до преобразования, то оно выполняется и после него. Например, на рис.7.6а, который иллюстрирует случай 1, количество черных узлов на пути от корня до поддеревьеви равно 3, как до, так и после преобразования (не забудьте о том, что узелх — дважды черный). Аналогично, количество черных узлов на пути от корня до любого из поддеревьев 7.6бравно 2, как до, так и после преобразования. На рис.7.6б подсчет должен включать значение с, равное значению атрибутаcolor корня показанного поддерева, которое может быть либоred, либоblack. Так, на пути от корня до поддерева количество черных узлов равно 2 плюс величина, равная 1, если с равноblack, и 0, еслис равноred; эта величина одинакова до и после выполнения преобразований. В такой ситуации после преобразования новый узелх имеет атрибутcolor, равный с, но реально это либо красно-черный узел (если с =RED),либо дважды черный (если с =BLACK). Прочие случаи могут быть проверены аналогичным способом .
Рис. 7.6. Возможные ситуации, возникающие в циклеwhile процедурыRB_ Delete_Fixup. Темные узлы имеют цветblack, темно-серые —red, а светлые могут иметь любой цвет (на рисунке показан какс иd). Греческими буквами показаны произвольные поддеревья.
Случай 1: узел w красный.
Случай 1 (строки 5-8 процедуры RB_DELETE_F[XUP и рис.7.6а) возникает, когда узелw ("брат" узлах) — красный. Посколькуw должен иметь черных потомков, можно обменять цветаw ир[х], а затем выполнить левый поворот вокруг р[х] без нарушения каких-либо красно-черных свойств. Новый "брат"х, до поворота бывший одним из потомковw, теперь черный. Таким путем случайI приводится к случаю 2,3 или 4.
Случаи 2, 3 и 4 возникают при черном узле w и отличаются друг от друга цветами дочерних по отношению кw узлов.
Случай 2: узел w черный, оба его дочерних узла черные.
В этом случае (строки 10-11 процедурыRB_DELETE_FlXUP и рис.7.66) оба дочерних узлаw черные. Поскольку узелw также черный, мы можем забрать черную окраскуу х и w, сделавх единожды черным,a w — красным. Для того чтобы компенсировать удаление черной окраски ух иw, мы можем добавить дополнительный черный цвет узлу р[х], который до этого мог быть как красным, так и черным. После этого будет выполнена следующая итерация цикла, в которой рольх будет играть текущий узелр[х]. Заметим, что если мы переходим к случаю 2 от случая 1, новый узелх — красно-черный, поскольку исходный узелр[х] был красным. Следовательно, значение с атрибутаcolor нового узлах равноRED и цикл завершается при проверке условия цикла. После этого новый узелхокрашивается в обычный черный цвет в строке 23.
Случай 3: узел w черный, его левый дочерний узел красный, а правый —черный.
В этом случае (строки 13-16 процедуры RB_DELETE_FlXUP и рис.7.6в) узелw черный, его левый дочерний узел красный, а правый-черный. Мы можем обменять цветаw и
left [w], а затем выполнить правый поворот вокругwбез нарушения каких-либо красно-черных свойств. Новым "братом" узлах после этого будет черный узел с красным правым дочерним узлом, и таким образом мы привели случай 3 к случаю 4.
Случай 4: узел w черный, его правый дочерний узел красный.
В этом случае (строки 17-21 процедуры RB_DELETE_FlXUP и рис.7.6г) узел w черный, а его правый дочерний узел-красный. Выполняя обмен цветов и левый поворот вокругр[х], мы можем устранить излишнюю черноту вх, делая его просто черным, без нарушения каких-либо красно-черных свойств. Присвоениех указателя на корень дерева приводит к завершению работы при проверке условия цикла при следующей итерации.
Анализ.
Чему равно время работы процедуры RB_DELETE? Поскольку высота дерева сп узлами равнаO(log2n), общее время работы процедуры без выполнения вспомогательной процедурыRB_DELETE_FlXUP равноO(log2n). В процедуреRB_DELETE_FIXUP в случаях 1, 3 и 4 завершение работы происходит после выполнения постоянного числа изменений цвета и не более трех поворотов. Случай 2 — единственный, после которого возможно выполнение очередной итерации циклаwhile, причем указательх перемещается вверх по дереву не более чемО (log 2n) раз, и никакие повороты при этом не выполняются. Таким образом, время работы процедурыRB_Delete_Fixup составляетО (log2n), причем она выполняет не более трех поворотов. Общее время работы процедурыRB_Delete, само собой разумеется, также равноО (log2 n).