АВЛ-дерево с O(1) бит в каждом узле — различия между версиями
Kyper (обсуждение | вклад) |
Kyper (обсуждение | вклад) |
||
Строка 2: | Строка 2: | ||
=== Идея === | === Идея === | ||
− | В обычной реализации АВЛ-дерева[http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE] в каждом узле мы хранили высоту этого узла. Так как высоты левых и правых поддеревьев в АВЛ-дереве отличаются максимум на <tex>1</tex>, то мы будем хранить не всю высоту дерева, а некоторое число, которое будет показывать | + | В обычной реализации АВЛ-дерева[http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE] в каждом узле мы хранили высоту этого узла. Так как высоты левых и правых поддеревьев в АВЛ-дереве отличаются максимум на <tex>1</tex>, то мы будем хранить не всю высоту дерева, а некоторое число, которое будет показывать, какое поддерево больше, или равны ли они, назовём ''фактор баланса''. Таким образом в каждом узле будет хранится <tex>1</tex> {{---}} если высота правого поддерева выше левого, <tex>0</tex> {{---}} если высоты равны, и <tex>-1</tex> {{---}} если правое поддерево выше левого. |
=== Операции === | === Операции === | ||
'''Операция добавления''' <br> | '''Операция добавления''' <br> | ||
− | Пусть нам надо добавить ключ <tex>t</tex>. Будем спускаться по дереву, как при поиске ключа <tex>t</tex>. Если мы стоим в вершине <tex>a</tex> и нам надо идти в поддерево, которого нет, то делаем ключ <tex>t</tex> листом, а вершину <tex>a</tex> его корнем. Пересчитываем баланс данного узла <tex>a</tex>. Дальше начинаем подниматься | + | Пусть нам надо добавить ключ <tex>t</tex>. Будем спускаться по дереву, как при поиске ключа <tex>t</tex>. Если мы стоим в вершине <tex>a</tex> и нам надо идти в поддерево, которого нет, то делаем ключ <tex>t</tex> листом, а вершину <tex>a</tex> его корнем. Пересчитываем баланс данного узла <tex>a</tex>. Если он оказался <tex>0</tex>, то высота поддерева с корнем в этой вершине не изменилась и пересчет балансов останавливается. Дальше начинаем подниматься вверх по дереву, исправляя балансы попутных узлов. Если мы поднялись в вершину <tex>i</tex> из левого поддерева, то баланс увеличивается на единицу, если из правого, то уменьшается на единицу. Если мы пришли в вершину и её баланс стал равным <tex>1</tex> или <tex>-1</tex>, то это значит, что высота поддерева изменилась и подъём продолжается. Если баланс вершины <tex>a</tex>, в которую мы собираемся идти из ее левого поддерева, равен <tex>1</tex>, то делается поворот для этой вершины <tex>a</tex>. Аналогично делаем поворот, если баланс вершины <tex>a</tex>, в которую мы идем из ее правого поддерева, равен <tex>-1</tex>. Если в результате изменения узла, фактор баланса стал равен нулю, то останавливаемся, иначе продолжаем подъём. |
'''Операция удаления''' <br> | '''Операция удаления''' <br> | ||
− | Если вершина - лист, то просто удалим её, иначе найдём ближайшую по значению вершину <tex>a</tex>, поменяем ее местами с удаляемой вершиной и удалим. От удалённой вершины будем подниматься вверх к корню и пересчитывать фактор баланса вершин. Если мы поднялись в вершину <tex>i</tex> из левого поддерева, то фактор баланса уменьшается на единицу, если из правого, то увеличивается на единицу. Если мы пришли в вершину и её баланс стал равным <tex>1</tex> или <tex>-1</tex>, то это значит, что высота поддерева не изменилась и подъём можно остановить. Если баланс вершины стал равным нулю, то высота поддерева уменьшилась и подъём нужно продолжить. Если баланс вершины <tex>a</tex>, в которую мы собираемся идти из ее левого поддерева, равен <tex>-1</tex>, то делается поворот для этой вершины <tex>a</tex>. Аналогично делаем поворот, если баланс вершины <tex>a</tex>, в которую мы идем из ее правого поддерева, равен <tex>1</tex>. Если в результате изменения узла, фактор баланса стал равен нулю, то подъём продолжается, иначе останавливается. | + | Если вершина {{---}} лист, то просто удалим её, иначе найдём ближайшую по значению вершину <tex>a</tex>, поменяем ее местами с удаляемой вершиной и удалим. От удалённой вершины будем подниматься вверх к корню и пересчитывать фактор баланса вершин. Если мы поднялись в вершину <tex>i</tex> из левого поддерева, то фактор баланса уменьшается на единицу, если из правого, то увеличивается на единицу. Если мы пришли в вершину и её баланс стал равным <tex>1</tex> или <tex>-1</tex>, то это значит, что высота поддерева не изменилась и подъём можно остановить. Если баланс вершины стал равным нулю, то высота поддерева уменьшилась и подъём нужно продолжить. Если баланс вершины <tex>a</tex>, в которую мы собираемся идти из ее левого поддерева, равен <tex>-1</tex>, то делается поворот для этой вершины <tex>a</tex>. Аналогично делаем поворот, если баланс вершины <tex>a</tex>, в которую мы идем из ее правого поддерева, равен <tex>1</tex>. Если в результате изменения узла, фактор баланса стал равен нулю, то подъём продолжается, иначе останавливается. |
=== Балансировка === | === Балансировка === | ||
− | Опишем операции балансировки, а именно малый левый поворот, большой левый поворот и случаи их возникновения. Балансировка нам нужна для операций добавления и удаления узла. Для исправления факторов баланса, достаточно знать факторы баланса двух(в случае большого поворота-трех) вершин перед поворотом, и исправить значения этих же вершин после поворота. Обозначим фактор баланса вершины <tex>i</tex> как <tex>balance[i]</tex>. Операции поворота делаются на том шаге, когда мы находимся в правом сыне вершины <tex>a</tex>, если мы производим операцию добавления, и в левом сыне, если мы производим операцию удаления. Вычисления производим заранее, чтобы не допустить значения <tex>2</tex> или <tex>-2</tex> в вершине <tex>a</tex>. | + | Опишем операции балансировки, а именно малый левый поворот, большой левый поворот и случаи их возникновения. Балансировка нам нужна для операций добавления и удаления узла. Для исправления факторов баланса, достаточно знать факторы баланса двух(в случае большого поворота {{---}} трех) вершин перед поворотом, и исправить значения этих же вершин после поворота. Обозначим фактор баланса вершины <tex>i</tex> как <tex>balance[i]</tex>. Операции поворота делаются на том шаге, когда мы находимся в правом сыне вершины <tex>a</tex>, если мы производим операцию добавления, и в левом сыне, если мы производим операцию удаления. Вычисления производим заранее, чтобы не допустить значения <tex>2</tex> или <tex>-2</tex> в вершине <tex>a</tex>. На каждой иллюстрации изображен один случай высот поддеревьев. Нетрудно убедиться, что в остальных случаях всё тоже будет корректно. |
{| border="1" cellpadding="5" cellspacing="0" | {| border="1" cellpadding="5" cellspacing="0" | ||
Строка 22: | Строка 22: | ||
|- | |- | ||
|'''Малое левое вращение''' | |'''Малое левое вращение''' | ||
− | | [[Файл: | + | | [[Файл:Avl_u1.jpg|2000x200px]] |
| | | | ||
'''1 вариант:''' <tex>balance[a] = -1</tex> и <tex>balance[b] = -1</tex> | '''1 вариант:''' <tex>balance[a] = -1</tex> и <tex>balance[b] = -1</tex> | ||
Строка 37: | Строка 37: | ||
|- | |- | ||
|'''Большое левое вращение''' | |'''Большое левое вращение''' | ||
− | | [[Файл: | + | | [[Файл:Avl_u2.jpg|2000x200px]] |
| | | | ||
'''1 вариант:''' <tex>balance[a] = -1</tex> , <tex>balance[b] = 1</tex> и <tex>balance[c] = 1</tex> | '''1 вариант:''' <tex>balance[a] = -1</tex> , <tex>balance[b] = 1</tex> и <tex>balance[c] = 1</tex> |
Версия 15:51, 6 июня 2015
Содержание
АВЛ-дерево с бит в каждом узле
Идея
В обычной реализации АВЛ-дерева[1] в каждом узле мы хранили высоту этого узла. Так как высоты левых и правых поддеревьев в АВЛ-дереве отличаются максимум на , то мы будем хранить не всю высоту дерева, а некоторое число, которое будет показывать, какое поддерево больше, или равны ли они, назовём фактор баланса. Таким образом в каждом узле будет хранится — если высота правого поддерева выше левого, — если высоты равны, и — если правое поддерево выше левого.
Операции
Операция добавления
Пусть нам надо добавить ключ . Будем спускаться по дереву, как при поиске ключа . Если мы стоим в вершине и нам надо идти в поддерево, которого нет, то делаем ключ листом, а вершину его корнем. Пересчитываем баланс данного узла . Если он оказался , то высота поддерева с корнем в этой вершине не изменилась и пересчет балансов останавливается. Дальше начинаем подниматься вверх по дереву, исправляя балансы попутных узлов. Если мы поднялись в вершину из левого поддерева, то баланс увеличивается на единицу, если из правого, то уменьшается на единицу. Если мы пришли в вершину и её баланс стал равным или , то это значит, что высота поддерева изменилась и подъём продолжается. Если баланс вершины , в которую мы собираемся идти из ее левого поддерева, равен , то делается поворот для этой вершины . Аналогично делаем поворот, если баланс вершины , в которую мы идем из ее правого поддерева, равен . Если в результате изменения узла, фактор баланса стал равен нулю, то останавливаемся, иначе продолжаем подъём.
Операция удаления
Если вершина — лист, то просто удалим её, иначе найдём ближайшую по значению вершину , поменяем ее местами с удаляемой вершиной и удалим. От удалённой вершины будем подниматься вверх к корню и пересчитывать фактор баланса вершин. Если мы поднялись в вершину из левого поддерева, то фактор баланса уменьшается на единицу, если из правого, то увеличивается на единицу. Если мы пришли в вершину и её баланс стал равным или , то это значит, что высота поддерева не изменилась и подъём можно остановить. Если баланс вершины стал равным нулю, то высота поддерева уменьшилась и подъём нужно продолжить. Если баланс вершины , в которую мы собираемся идти из ее левого поддерева, равен , то делается поворот для этой вершины . Аналогично делаем поворот, если баланс вершины , в которую мы идем из ее правого поддерева, равен . Если в результате изменения узла, фактор баланса стал равен нулю, то подъём продолжается, иначе останавливается.
Балансировка
Опишем операции балансировки, а именно малый левый поворот, большой левый поворот и случаи их возникновения. Балансировка нам нужна для операций добавления и удаления узла. Для исправления факторов баланса, достаточно знать факторы баланса двух(в случае большого поворота — трех) вершин перед поворотом, и исправить значения этих же вершин после поворота. Обозначим фактор баланса вершины
как . Операции поворота делаются на том шаге, когда мы находимся в правом сыне вершины , если мы производим операцию добавления, и в левом сыне, если мы производим операцию удаления. Вычисления производим заранее, чтобы не допустить значения или в вершине . На каждой иллюстрации изображен один случай высот поддеревьев. Нетрудно убедиться, что в остальных случаях всё тоже будет корректно.
Примеры
Ниже приведены примеры добавления и удаления вершины с подписанными изменениями факторов баланса каждой вершины.