Бинарная куча — различия между версиями

Материал из Public ATP Wiki
Перейти к: навигация, поиск
(Новая страница: «== Heap(Куча) == Heap implements priority queue.<br> (Куча реализует очередь c приоритетами) === Binary heap(Бинарная…»)
 
 
(не показаны 4 промежуточные версии этого же участника)
Строка 1: Строка 1:
== Heap(Куча) ==
+
== Куча ==
Heap implements priority queue.<br>
+
Куча реализует очередь c приоритетами
(Куча реализует очередь c приоритетами)
 
  
=== Binary heap(Бинарная куча) ===
+
=== Бинарная куча ===
Our binary heap needs this functions:<br>
+
==== Общее представление ====
(Наша бинарная куча должна иметь эти функции:)<br>
+
Наша бинарная куча должна иметь эти функции:<br>
 
*Add(key, value)<br>
 
*Add(key, value)<br>
 
(Вставка(ключ, значение))<br>
 
(Вставка(ключ, значение))<br>
 
*Extract(key, value)<br>
 
*Extract(key, value)<br>
(Удаление(ключ, значение))<br>
+
(Удаление(ключ, значение)<br>
[[Файл:BinaryHeapLogo.png|200px|thumb|left|описание]]
+
Всё работает за $ \Theta(\logn) $
 +
[[Файл:BinaryHeapLogo.png|400px|без|thumb|left| двоичное дерево полное, означает, что заполнено слева направо и уровень не больше 1]]
 +
----
 +
==== Вставка ====
 +
Посмотрим как можно добавить новый элемент:<br>
 +
В начале просто допишем наш элемент k как ребёнка к любому нашему родителю, у которого слева(справа) нет ребёнка<br>
 +
Но просто добавить его будет таким себе занятием, ибо может быть такое, что всё поломается, следовательно, нужно всё исправить<br>
 +
Ибо нам необходимо, чтобы для каждого нашей ноды работал HeapProperity:<br>
 +
[[Файл:HeapProperity.png|300px|без|thumb|left| Heap Properity]]<br>
 +
Для этого применим алгоритм SiftUp(i):
 +
<source lang="c++">
 +
void SiftUp(Node* i)
 +
{
 +
  if(i != root)
 +
    while(p[i]->key > i->key)
 +
      swap(i)
 +
  return;
 +
}
 +
</source>
 +
При первом заходе в цикл меняется что-то только, если наше новое <b>k</b> меньше чем <b>a</b>, <br>
 +
Следовательно именно такой случай и рассмотрим:<br>
 +
[[Файл:SiftUp.png|500px|без|thumb|left| Изменения после первого захода в цикл SiftUp]]<br>
 +
Здесь, всё что оказалось ниже, чем <b>k</b> будет верно, следовательно нужно проверять выше<br>
 +
Поэтому если <b>d</b> меньше, чем <b>k</b>, то снова заходим в цикл, иначе мы завершили<br>
 +
----
 +
==== Извлечение ====
 +
Извлечение у нас будет осуществляться алгоритмом SiftDown<br>
 +
Для этого нам необходимо поменять <b>root</b> с одним из <b>leaf</b><br>
 +
[[Файл:SwapRL.png|800px|без|thumb|left]]
 +
После чего применить функцию SiftDown:<br>
 +
[[Файл:SiftDown.png|500px|без|thumb|left| Условия для срабатывания SiftDown]]
 +
Если родитель получается больше, чем дитё, тогда:<br>
 +
[[Файл:SiftDown2.png|500px|без|thumb|left| Если условия выполняются]]
 +
$ \Theta(\log(height)) $ - SiftUp и SiftDown<br>
 +
----
 +
==== Хранение ====
 +
Как же хранить нашу кучу?<br>
 +
Ответ может удивить, ведь храним мы в обычном массиве(векторе)<br>
 +
[[Файл:HeapVector.png|800px|без|thumb|left| Каждый <b>leaf</b> является <b>2*i + 1</b> или <b>2*i + 2</b> индексом <b>parent</b>]]
 +
Соответственно в обычном представлении: <br>LeftChild(i) = 2*i + 1;<br>RightChild(i) = 2*i + 2;<br>
 +
Но некоторые умельцы начинают массив сразу с <b><i>1</i></b>, чтобы использовалось меньше машинных операций(теперь индексы детей будут вычисляться как <b>2*i</b> и <b>2*i + 1</b>)<br>
 +
==== Heapify ====
 +
$ \Theta(\log(n)) $<br>
 +
Heapify - преобразование массива к куче<br>
 +
А работает это за $ \Theta(\log(n)) $, так как:<br>
 +
<source lang="c++">
 +
for (i:[int/2...0])
 +
    SiftDown(i);
 +
</source>
 +
n/2*1 + n/4 + n/8 * 3 + ... <br>
 +
$ n * \sum_n=1^{\infty} 1/{2^{n}} \smash 2 $

Текущая версия на 08:45, 7 марта 2020

Куча

Куча реализует очередь c приоритетами

Бинарная куча

Общее представление

Наша бинарная куча должна иметь эти функции:

  • Add(key, value)

(Вставка(ключ, значение))

  • Extract(key, value)

(Удаление(ключ, значение)
Всё работает за $ \Theta(\logn) $

двоичное дерево полное, означает, что заполнено слева направо и уровень не больше 1

Вставка

Посмотрим как можно добавить новый элемент:
В начале просто допишем наш элемент k как ребёнка к любому нашему родителю, у которого слева(справа) нет ребёнка
Но просто добавить его будет таким себе занятием, ибо может быть такое, что всё поломается, следовательно, нужно всё исправить
Ибо нам необходимо, чтобы для каждого нашей ноды работал HeapProperity:

Heap Properity

Для этого применим алгоритм SiftUp(i):

void SiftUp(Node* i)
{
  if(i != root)
    while(p[i]->key > i->key)
      swap(i)
  return;
}

При первом заходе в цикл меняется что-то только, если наше новое k меньше чем a,
Следовательно именно такой случай и рассмотрим:

Изменения после первого захода в цикл SiftUp

Здесь, всё что оказалось ниже, чем k будет верно, следовательно нужно проверять выше
Поэтому если d меньше, чем k, то снова заходим в цикл, иначе мы завершили


Извлечение

Извлечение у нас будет осуществляться алгоритмом SiftDown
Для этого нам необходимо поменять root с одним из leaf

SwapRL.png

После чего применить функцию SiftDown:

Условия для срабатывания SiftDown

Если родитель получается больше, чем дитё, тогда:

Если условия выполняются

$ \Theta(\log(height)) $ - SiftUp и SiftDown


Хранение

Как же хранить нашу кучу?
Ответ может удивить, ведь храним мы в обычном массиве(векторе)

Каждый leaf является 2*i + 1 или 2*i + 2 индексом parent

Соответственно в обычном представлении:
LeftChild(i) = 2*i + 1;
RightChild(i) = 2*i + 2;
Но некоторые умельцы начинают массив сразу с 1, чтобы использовалось меньше машинных операций(теперь индексы детей будут вычисляться как 2*i и 2*i + 1)

Heapify

$ \Theta(\log(n)) $
Heapify - преобразование массива к куче
А работает это за $ \Theta(\log(n)) $, так как:

for (i:[int/2...0])
    SiftDown(i);

n/2*1 + n/4 + n/8 * 3 + ...
$ n * \sum_n=1^{\infty} 1/{2^{n}} \smash 2 $