Найти тему
Недалёкий разбор

Приближение дерева - бинарная куча - Python - структура - алгоритмы

Классический способ реализации очереди с приоритетом - использовать структуру данных под названием двоичная куча. Она позволит нам извлекать из неё элементы за O(log(n).

Кучей называется дерево, в котором любой элемент не меньше своего родителя.

  • Если любой узел всегда больше дочернего узла (узлов), а ключ корневого узла является наибольшим среди всех остальных узлов, это max-куча.
  • Если любой узел всегда меньше дочернего узла (узлов), а ключ корневого узла является наименьшим среди всех остальных узлов, это min-куча.

три основные операции, производимые с кучей:

  • получение минимума (root);
  • извлечение минимума (pop);
  • добавление элемента (append);

Так как мы говорим про двоичную кучу, то к ней накладываются дополнительные ограничения:

  • глубина всех листьев отличается не более, чем на 1;
  • последний слой заполняется слева направо без пропусков;
  • степень ветвления два потомка

Двоичная куча (структура данных) — это полное двоичное дерево, удовлетворяющее свойству кучи: если узел A — это родитель узла B, то ключ узла A ≥ ключ узла B.

пример min binary heap

Реализация min heap на Python:

class BinHeap:
def __init__(self):
self.heapList = [0]
self.currentSize = 0
# инициализируем дерево и длину дерева

def percUp(self,i):
while i // 2 > 0:
if self.heapList[i] < self.heapList[i // 2]:
tmp = self.heapList[i // 2]
self.heapList[i // 2] = self.heapList[i]
self.heapList[i] = tmp
i = i // 2

def insert(self,k):
self.heapList.append(k)
self.currentSize = self.currentSize + 1
self.percUp(self.currentSize)

расписываем метод вставки нового элемента: с помощью функции append добавляем элемент, обновляем длину нашего дерева, и вызываем метод percUp для проверки соответствия нахождения элемента, в соответствии с minHeap: для вставляемого элемента i, i // 2 всегда будет является его родителем, если значение нового элемента меньше значения корня, то меняем их местами, до тех пор пока не дойдём до корня.

def percDown(self,i):
while (i * 2) <= self.currentSize:
mc = self.minChild(i)
if self.heapList[i] > self.heapList[mc]:
tmp = self.heapList[i]
self.heapList[i] = self.heapList[mc]
self.heapList[mc] = tmp
i = mc

def minChild(self,i):
if i * 2 + 1 > self.currentSize:
return i * 2
else:
if self.heapList[i*2] < self.heapList[i*2+1]:
return i * 2
else:
return i * 2 + 1

def delMin(self):
retval = self.heapList[1]
self.heapList[1] = self.heapList[self.currentSize]
self.currentSize = self.currentSize - 1
self.heapList.pop()
self.percDown(1)
return retval

расписываем метод удаления минимального элемента, то есть корня дерева, заменяем его последним элементом и вызываем метод percDown, так как мы не знаем нарушились ли принципы кучи (корень = минимальный элемент), который вызывает метод minChild, для определения с каким потомком сравнивать значения родителя (смотрим на картинку и видим, что если правого потомка не существует, то выбираем левого, если же он существует, то сравниваем значения правого и левого), дальше выбрав потомка мы возвращаемся к методу percDown, который меняет местами родителя и ребёнка до тех пор, пока свойства кучи не будет возвращено и (i * 2) не станет больше длины дерева.

def buildHeap(self,alist):
i = len(alist) // 2
self.currentSize = len(alist)
self.heapList = [0] + alist[:]
while (i > 0):
self.percDown(i)
i = i - 1

расписываем метод создания дерева на основе списка, длина дерева равняется длине списка, для проверки свойств кучи достаточно пройти половины длины списка len(alist) // 2, вызывая self.percDown(i). Хотя мы начинаем с середины дерева, метод percDown гарантирует, что наибольший элемент всегда спустится вниз по дереву. Поскольку куча - полное двоичное дерево, любой прошедший половину пути узел будет листом и, следовательно, не иметь потомков, то есть за len(i//2) мы охватываем всех родителей дерева. и так циклом до корня...

Рисунок 4: Создание кучи из списка [9, 6, 5, 2, 3]
Рисунок 4: Создание кучи из списка [9, 6, 5, 2, 3]


bh = BinHeap()
bh.buildHeap([9,5,6,2,3])
print(bh.delMin())

Код взят с книги Брэда Миллер и Дэвид Рэнума - Структура и Алгоритмы