Левосторонние красно-чёрные деревья — различия между версиями
(→Удаление максимума) |
(→Удаление максимума) |
||
| Строка 134: | Строка 134: | ||
Соответственно спускаясь вниз по дереву необходимо поддерживать следующий инвариант : количество потомков узла не должно быть ровно <tex>2</tex>-м. | Соответственно спускаясь вниз по дереву необходимо поддерживать следующий инвариант : количество потомков узла не должно быть ровно <tex>2</tex>-м. | ||
[[File:changeNode.png|600px|thumb|center| ]] | [[File:changeNode.png|600px|thumb|center| ]] | ||
| + | |||
| + | Будем поддерживать инвариант : Для любого узла либо сам узел, либо правый предок узла '''красный'''. | ||
| + | Заметим, что удалять лист легче, чем внутренний узел. | ||
| + | |||
| − | '''void''' deleteMin() | + | '''void''' deleteMin() |
| − | + | root = deleteMin(root); | |
| − | + | root.color = BLACK; | |
'''Node''' deleteMin(h : '''Node''') | '''Node''' deleteMin(h : '''Node''') | ||
| − | + | if (h.left == ''null'') '''return''' ''null''; | |
| − | + | '''if !'''isRed(h.left) '''&& !'''isRed(h.left.left) | |
| − | + | h = moveRedLeft(h); | |
| − | + | h.left = deleteMin(h.left); | |
'''return''' fixUp(h); | '''return''' fixUp(h); | ||
| Строка 151: | Строка 155: | ||
'''void''' deleteMin(): | '''void''' deleteMin(): | ||
| − | + | root = deleteMin(root) | |
| − | + | root.color = BLACK | |
'''Node''' deleteMin( h : '''Node'''): | '''Node''' deleteMin( h : '''Node'''): | ||
| − | + | '''if''' h.left == ''null'' | |
| − | + | '''return''' ''null'' | |
| − | + | '''if''' !isRed(h.left) '''&&''' !isRed(h.left.left) | |
| − | + | h = moveRedLeft(h) | |
| − | + | h.left = deleteMin(h.left) | |
'''return''' fixUp(h) | '''return''' fixUp(h) | ||
| Строка 166: | Строка 170: | ||
'''Node''' moveRedLeft('''Node''' : h): | '''Node''' moveRedLeft('''Node''' : h): | ||
| − | + | colorFlip(h): | |
| − | + | '''if''' isRed(h.right.left) | |
| − | + | h.right = rotateRight(h.right) | |
| − | + | h = rotateLeft(h) | |
| − | + | colorFlip(h) | |
| − | + | '''return''' h | |
'''Node''' moveRedRight(h :'''Node''' ): | '''Node''' moveRedRight(h :'''Node''' ): | ||
| − | + | colorFlip(h) | |
| − | + | '''if''' isRed(h.left.left)) | |
| − | + | h = rotateRight(h) | |
| − | + | colorFlip(h) | |
| − | + | '''return''' h | |
| − | + | '''void''' delete(key : '''Key'''): | |
| − | + | root = delete(root, key) | |
| − | + | root.color = BLACK | |
'''Node''' delete('''Node''' : h, '''Key''' : key): | '''Node''' delete('''Node''' : h, '''Key''' : key): | ||
| − | + | '''if''' key.compareTo(h.key) < 0) | |
'''if''' !isRed(h.left) '''&&''' !isRed(h.left.left) | '''if''' !isRed(h.left) '''&&''' !isRed(h.left.left) | ||
| − | + | h = moveRedLeft(h) | |
h.left = delete(h.left, key) | h.left = delete(h.left, key) | ||
| − | + | '''else''' | |
| − | + | '''if''' isRed(h.left) | |
| − | + | h = rotateRight(h) | |
| − | + | '''if''' key.compareTo(h.key) == 0 '''&&''' (h.right == null) | |
| − | + | '''return''' ''null'' | |
| − | + | '''if''' !isRed(h.right) '''&&''' !isRed(h.right.left) | |
| − | + | h = moveRedRight(h) | |
| − | + | '''if''' key.compareTo(h.key) == 0 | |
| − | + | h.val = get(h.right, min(h.right).key) | |
| − | + | h.key = min(h.right).key | |
| − | + | h.right = deleteMin(h.right) | |
| − | + | '''else''' | |
| − | + | h.right = delete(h.right, key) | |
| − | + | '''return''' fixUp(h) | |
Версия 15:53, 6 апреля 2018
| Определение: |
| Left-leaning Red-Black Trees — модификация красно-черных деревьев, имеющая ряд преимуществ на классической структурой. Разработана Робертом Соджевиском в 2008 году. |
Содержание
Преимущества
- необходимо менее 80 строчек кода для реализации структуры
- более быстрая вставка, удаление элементов
- простота
Вращения
Чтобы поддерживать красно-черные двоичное деревья поиска необходимо соблюдать следующие инвариантные свойства при вставке и удалении:
- Ни один обход от корня до листьев дерева не содержит двух последовательных красных узлов.
- Количество черных узлов на каждом таком пути одинаково.
Из этих инвариантов следует, что длина каждого пути от корня до листьев в красно-черном дереве с узлами не превышает .
Основные операции, используемые алгоритмами сбалансированного дерева для поддержания баланса при вставке и удалении, называются вращениями. Эти операции трансформируют -узел,левый потомок которого окрашен в красный, в -узел, правый потомок которого окрашен в красный и наоборот. Вращения сохраняют два указанных выше инварианта, не изменяют поддеревья узла.
Псевокод
Node rotateRight( h : Node) :
x = h.left
h.left= x.right
x.right= h
x.color = h.color
h.color = RED
return x
Node rotateLeft( h : Node) :
x = h.right
h.right = x.left
x.left = h
x.color = h.color
h.color = RED
return x
Переворот цветов
В красно-черных деревьях используется такая операция как Переворот цветов , которая инвертирует цвет узла и двух его детей. Она не изменяет количество черных узлов при любом обходе от корня до листьев дерева, но может привести к появлению двух последовательных красных узлов.
void flipColors( h : Node h) :
h.color = ! h.color
h.left.color = ! h.left.color
h.right.color = h.right.color
Вставка
Вставка в LLRB базируется на простых операциях:
- Вставка нового узла к листу дерева:
if (h == null)
return new Node(key, value, RED);
- Расщепление узла с -я потомками:
if (isRed(h.left) && isRed(h.right))
colorFlip(h);
- Принудительное вращение влево:
if (isRed(h.right))
h = rotateLeft(h);
- Балансировка узла с -я потомками:
if (isRed(h.left) && isRed(h.left.left))
h = rotateRight(h);
Псевдокод
void insert( key : Key, value : Value ):
root = insert(root, key, value)
root.color = BLACK
Node insert( h : Node, key : Key, value : Value):
//Вставка нового узла к листу дерева
if h == null
return new Node(key, value)
//Расщепление узла с -я потомками
if isRed(h.left) && isRed(h.right)
colorFlip(h)
//Стандартная вставка в дереве поиска
int cmp = key.compareTo(h.key)
if cmp == 0
h.val = value
else
if cmp < 0
h.left = insert(h.left, key, value)
else
h.right = insert(h.right, key, value)
//Принудительное вращение влево
if isRed(h.right) && !isRed(h.left)
h = rotateLeft(h)
////Балансировка узла с -я потомками
if isRed(h.left) && isRed(h.left.left)
h = rotateRight(h)
return h
Поиск
Псевдокод
Value search(key : Key):
Node x = root
while x != null
int cmp = key.compareTo(x.key)
if cmp == 0
return x.val
else
if cmp < 0
x = x.left
else
if cmp > 0
x = x.right
return null
Удаление
- Использование и сохраняют баланс черной связи.
- После удаления необходимо исправить правые красные связи и устранить узлы с я потомками
//Исправление правых красных связей
Node fixUp(h : Node){
if (isRed(h.right))
h = rotateLeft(h);
//Вращение пары
if (isRed(h.left) && isRed(h.left.left))
h = rotateRight(h);
//Балансировка узла с -я потомками
if (isRed(h.left) && isRed(h.right))
colorFlip(h);
return h;
}
Удаление максимума
- Спускаемся вниз по правому краю дерева.
- Если поиск заканчивается на узле с -мя или -ю потомками, просто удаляем узел.
- Удаление узла с -я потомками разрушает баланс
Соответственно спускаясь вниз по дереву необходимо поддерживать следующий инвариант : количество потомков узла не должно быть ровно -м.
Будем поддерживать инвариант : Для любого узла либо сам узел, либо правый предок узла красный. Заметим, что удалять лист легче, чем внутренний узел.
void deleteMin()
root = deleteMin(root);
root.color = BLACK;
Node deleteMin(h : Node)
if (h.left == null) return null;
if !isRed(h.left) && !isRed(h.left.left)
h = moveRedLeft(h);
h.left = deleteMin(h.left);
return fixUp(h);
Delete-the-minimum code for LLRB 2-3 trees
void deleteMin():
root = deleteMin(root)
root.color = BLACK
Node deleteMin( h : Node):
if h.left == null
return null
if !isRed(h.left) && !isRed(h.left.left)
h = moveRedLeft(h)
h.left = deleteMin(h.left)
return fixUp(h)
Node moveRedLeft(Node : h):
colorFlip(h):
if isRed(h.right.left)
h.right = rotateRight(h.right)
h = rotateLeft(h)
colorFlip(h)
return h
Node moveRedRight(h :Node ):
colorFlip(h)
if isRed(h.left.left))
h = rotateRight(h)
colorFlip(h)
return h
void delete(key : Key):
root = delete(root, key)
root.color = BLACK
Node delete(Node : h, Key : key):
if key.compareTo(h.key) < 0)
if !isRed(h.left) && !isRed(h.left.left)
h = moveRedLeft(h)
h.left = delete(h.left, key)
else
if isRed(h.left)
h = rotateRight(h)
if key.compareTo(h.key) == 0 && (h.right == null)
return null
if !isRed(h.right) && !isRed(h.right.left)
h = moveRedRight(h)
if key.compareTo(h.key) == 0
h.val = get(h.right, min(h.right).key)
h.key = min(h.right).key
h.right = deleteMin(h.right)
else
h.right = delete(h.right, key)
return fixUp(h)