В начале урока мы говорили, что дерево можно хранить в массиве. Разберёмся, как это работает.
Рассмотрим приведённый выше пример для неубывающей пирамиды.
Пирамида будет храниться в массиве так:
Скопировать кодPYTHON
[3, 6, 5, 11, 23, 16, 9, 96, 17]
Благодаря структуре пирамиды по номеру дочернего узла можно определить номер его родителя, а по номеру родителя — индексы его потомков, если они существуют.
Индекс корня дерева всегда равен 1. Это отличается от стандартной нумерации массивов. В них индексация начинается с 0.
Корень с индексом 1 упрощает формулы поиска остальных элементов. Рассмотрим их.
- Индекс родителя узла i равен [2i], где [x] означает округление до ближайшего целого числа, которое меньше или равно x.
- Индекс левого потомка узла i равен 2i.
- Индекс правого потомка узла i равен 2i+1.
Посмотрим на примере:
Определим индексы потомков для корня:
Индекс левого потомка
i равен
2i, то есть 2.
Индекс правого потомка
i равен
2i+1, то есть 3.
Всё верно!
Теперь определим родителя для узла с номером 5.
Индекс родителя узла
i равен
[2i], то есть [2,5]. Округлим 2,5 до целого числа и получим 2.
Верно! Мы убедились в корректности формул.
Благодаря свойствам пирамиды нам не нужно хранить в памяти указатели на детей и родителей. Это существенно экономит память.
Разберёмся, как вставлять и удалять элементы в кучу, не нарушая её свойств.
Добавим элемент в конец кучи:
После добавления элемента куча осталась бинарным полным деревом.
Но она больше не удовлетворяет свойству, по которому значения дочерних узлов не больше значений родительских, ведь 33 больше 14.
Разберёмся, как это исправить.
Если дочерний узел больше родительского, поменяем их местами.
В примере достаточно одной итерации и пирамида снова удовлетворяет своим свойствам.
Вставляя любой другой элемент, нужно проделывать операцию обмена до тех пор, пока куча не станет упорядоченной. В худшем случае новый элемент встанет на вершину пирамиды.
Теперь извлечём самый приоритетный элемент из кучи.
Удаление элемента нарушило свойство упорядоченности бинарной кучи. Восстановим его. Функцию восстановления свойств пирамиды обычно называют heapify.
Рассмотрим текущий узел и его дочерние элементы.
Если текущий элемент — самый приоритетный из них, ничего не меняем.
Иначе меняем текущий элемент с максимальным приоритетным — он должен оказаться выше.
Мы поменяли местами вершину с левым потомком. Правую часть дерева не трогали, поэтому на следующем шаге рассматривать её не нужно.
Проверим свойство упорядоченности в левой части дерева. Если нужно, скорректируем тем же алгоритмом.
Три элемента: 14, 17 и 45 расположены в неправильном порядке. 14 меньше 17 и 45.
Снова поменяем местами элемент в текущей вершине с максимальным.
У текущего элемента нет дочерних узлов. Значит, мы восстановили свойство бинарной кучи.
Таким образом, есть два критерия завершения процедуры восстановления:
- Если значение текущего узла больше, чем значение его потомков.
- Если у текущего узла отсутствуют потомки.
Мы можем добавлять и удалять элементы, восстанавливая свойство упорядоченности. При добавлении мы поднимаем элемент по пирамиде вверх. А при удалении — спускаем его вниз.
Гоша: Немного сложно, но я опять понял! Так, а что по поводу сортировки?
Тимофей: Скоро всё узнаете!