Объединение/объединение/соединение двух деревьев AVL
-
19-09-2019 - |
Вопрос
Предположим, что у меня есть два дерева AVL и что каждый элемент первого дерева меньше любого элемента второго дерева.Каков наиболее эффективный способ объединить их в одно дерево AVL?Я искал везде, но не нашел ничего полезного.
Решение
Предполагая, что вы можете уничтожить входные деревья:
- удалите самый правый элемент левого дерева и используйте его для создания нового корневого узла, левым дочерним элементом которого является левое дерево, а правым дочерним элементом является правое дерево:О (логарифм n)
- определите и установите коэффициент балансировки этого узла:О(логарифм n).При (временном) нарушении инварианта коэффициент баланса может находиться вне диапазона {-1, 0, 1}
- поверните, чтобы вернуть коэффициент баланса в диапазон:O(log n) вращений:О (логарифм n)
Таким образом, вся операция может быть выполнена за O(log n).
Редактировать:Если подумать, проще рассуждать о вращениях в следующем алгоритме.Это также вполне вероятно быстрее:
- Определим высоту обоих деревьев:О(логарифм n).
Предполагая, что правое дерево выше (другой случай симметричен): - удалить самый правый элемент из
left
дерево (при необходимости поворачивая и корректируя его вычисленную высоту).Позволятьn
быть этим элементом.О (логарифм n) - В правом дереве перемещайтесь влево, пока не дойдете до узла, поддерево которого не более чем на 1 выше, чем
left
.Позволятьr
быть этим узлом.О (логарифм n) замените этот узел новым узлом со значением n и поддеревьями
left
иr
.О(1)
По построению новый узел является AVL-сбалансированным, а его поддерево на 1 выше, чемr
.соответственно увеличить баланс своего родителя.О(1)
- и выполните повторную балансировку, как если бы вы это сделали после вставки.О (логарифм n)
Другие советы
Одно очень простое решение (которое работает без каких-либо предположений об отношениях между деревьями) заключается в следующем:
- Выполните сортировку слиянием обоих деревьев в один объединенный массив (одновременно перебирайте оба дерева).
- Постройте дерево AVL из массива — возьмите средний элемент за корень и рекурсивно примените его к левой и правой половинам.
Оба шага занимают O(n).Основная проблема заключается в том, что для этого требуется O(n) дополнительного пространства.
Лучшее решение этой проблемы, которое я читал, можно найти здесь.Очень близко к меритонответ, если вы исправите эту проблему:
На третьем шаге алгоритма перемещается влево, пока не дойдете до узла, поддерево которого имеет ту же высоту, что и левое дерево.Это не всегда возможно (см. изображение контрпримера).Правильный способ сделать этот шаг — найти два поддерева с высотой h
или h+1
где h
высота левого дерева
Я подозреваю, что вам просто придется пройтись по одному дереву (надеюсь, меньшему) и индивидуально добавить каждый из его элементов в другое дерево.Операции вставки/удаления AVL не предназначены для одновременного добавления всего поддерева.