<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=188.162.65.63&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=188.162.65.63&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/188.162.65.63"/>
		<updated>2026-05-08T19:52:01Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D0%BE%D0%B8%D1%87%D0%BD%D0%B0%D1%8F_%D0%BA%D1%83%D1%87%D0%B0&amp;diff=39854</id>
		<title>Двоичная куча</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D0%BE%D0%B8%D1%87%D0%BD%D0%B0%D1%8F_%D0%BA%D1%83%D1%87%D0%B0&amp;diff=39854"/>
				<updated>2014-07-22T19:56:20Z</updated>
		
		<summary type="html">&lt;p&gt;188.162.65.63: /* siftUp */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Определение==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Двоичная куча''' или '''пирамида''' — такое двоичное [[Дерево, эквивалентные определения|подвешенное дерево]], для которого выполнены следующие три условия:&lt;br /&gt;
&lt;br /&gt;
* Значение в любой вершине не меньше, (если куча для максимума), чем значения её потомков.&lt;br /&gt;
* На &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ом слое &amp;lt;tex&amp;gt;2^i&amp;lt;/tex&amp;gt; вершин, кроме последнего. Слои нумеруются с нуля.&lt;br /&gt;
* Последний слой заполнен слева направо (как показано на рисунке)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Heap.png|thumb|325px|Пример кучи для максимума]]&lt;br /&gt;
&lt;br /&gt;
Удобнее всего двоичную кучу хранить в виде массива &amp;lt;tex&amp;gt;A[0..n-1]&amp;lt;/tex&amp;gt;, у которого нулевой элемент, &amp;lt;tex&amp;gt;A[0]&amp;lt;/tex&amp;gt; — элемент в корне, а потомками элемента &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; являются &amp;lt;tex&amp;gt;A[2i+1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;A[2i+2]&amp;lt;/tex&amp;gt;. Высота кучи определяется как высота двоичного дерева. То есть она равна количеству рёбер в самом длинном простом пути, соединяющем корень кучи с одним из её листьев. Высота кучи есть &amp;lt;tex&amp;gt;O(\log{N})&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;N&amp;lt;/tex&amp;gt; — количество узлов дерева.&lt;br /&gt;
&lt;br /&gt;
Чаще всего используют кучи для минимума (когда предок не больше детей) и для максимума (когда предок не меньше детей).&lt;br /&gt;
&lt;br /&gt;
Двоичные кучи используют, например, для того, чтобы извлекать минимум из набора чисел за &amp;lt;tex&amp;gt;O(\log{N})&amp;lt;/tex&amp;gt;. Они являются частным случаем приоритетных очередей.&lt;br /&gt;
&lt;br /&gt;
==Базовые процедуры==&lt;br /&gt;
&lt;br /&gt;
===Восстановление свойств кучи===&lt;br /&gt;
&lt;br /&gt;
Если в куче изменяется один из элементов, то она может перестать удовлетворять свойству упорядоченности. Для восстановления этого свойства служат процедуры &amp;lt;tex&amp;gt; \mathrm {siftDown} &amp;lt;/tex&amp;gt; (просеивание вниз)&lt;br /&gt;
и &amp;lt;tex&amp;gt; \mathrm {siftUp} &amp;lt;/tex&amp;gt; (просеивание вверх). &lt;br /&gt;
Если значение измененного элемента увеличивается, то свойства кучи восстанавливаются функцией &amp;lt;tex&amp;gt; \mathrm {siftDown} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
Работа процедуры: если &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й элемент меньше, чем его сыновья, всё поддерево уже является кучей, и делать ничего не надо. В противном случае меняем местами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й элемент с наименьшим из его сыновей, после чего выполняем &amp;lt;tex&amp;gt; \mathrm {siftDown} &amp;lt;/tex&amp;gt; для этого сына.&lt;br /&gt;
Процедура выполняется за время &amp;lt;tex&amp;gt;O(\log{N})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
====siftDown====&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 '''function''' siftDown(i : '''int'''):&lt;br /&gt;
     '''while''' 2 * i + 1 &amp;lt;tex&amp;gt;&amp;lt;&amp;lt;/tex&amp;gt; A.heapSize     &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;// heapSize {{---}} количество элементов в куче&amp;lt;/font&amp;gt;&lt;br /&gt;
         left = 2 * i + 1             &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;// left {{---}} левый сын&amp;lt;/font&amp;gt;&lt;br /&gt;
         right = 2 * i + 2            &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;// right {{---}} правый сын&amp;lt;/font&amp;gt;&lt;br /&gt;
         j = left&lt;br /&gt;
         '''if''' right &amp;lt;tex&amp;gt;&amp;lt;&amp;lt;/tex&amp;gt; A.heapSize '''and''' A[right] &amp;lt;tex&amp;gt;&amp;lt;&amp;lt;/tex&amp;gt; A[left]&lt;br /&gt;
             j = right&lt;br /&gt;
         '''if''' A[i] &amp;lt;tex&amp;gt;\leqslant&amp;lt;/tex&amp;gt; A[j]&lt;br /&gt;
             '''break'''&lt;br /&gt;
         swap(A[i], A[j])&lt;br /&gt;
         i = j&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====siftUp====&lt;br /&gt;
Если значение измененного элемента уменьшается, то свойства кучи восстанавливаются функцией &amp;lt;tex&amp;gt; \mathrm {siftUp} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Работа процедуры: если элемент больше своего отца, условие 1 соблюдено для всего дерева, и больше ничего делать не нужно. Иначе, мы меняем местами его с отцом. После чего выполняем &amp;lt;tex&amp;gt; \mathrm {siftUp} &amp;lt;/tex&amp;gt;&lt;br /&gt;
для этого отца. Иными словами, слишком маленький элемент всплывает наверх.&lt;br /&gt;
Процедура выполняется за время &amp;lt;tex&amp;gt;O(\log{N})&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 '''function''' siftUp(i : '''int'''):&lt;br /&gt;
     '''while''' A[i] &amp;lt;tex&amp;gt;&amp;lt;&amp;lt;/tex&amp;gt; A[(i - 1) / 2]     &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;// i &amp;lt;tex&amp;gt;==&amp;lt;/tex&amp;gt; 0 {{---}} мы в корне&amp;lt;/font&amp;gt;&lt;br /&gt;
         swap(A[i], A[(i - 1) / 2])&lt;br /&gt;
         i = (i - 1) / 2&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Извлечение минимального элемента===&lt;br /&gt;
&lt;br /&gt;
Выполняет извлечение минимального элемента из кучи за время &amp;lt;tex&amp;gt;O(\log{N})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Извлечение выполняется в четыре этапа:&lt;br /&gt;
# Значение корневого элемента (он и является минимальным) сохраняется для последующего возврата.&lt;br /&gt;
# Последний элемент копируется в корень, после чего удаляется из кучи.&lt;br /&gt;
# Вызывается &amp;lt;math&amp;gt; \mathrm {siftDown} &amp;lt;/math&amp;gt; для корня.&lt;br /&gt;
# Сохранённый элемент возвращается.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 '''int''' extractMin():&lt;br /&gt;
     '''int''' min = A[0]&lt;br /&gt;
     A[0] = A[A.heapSize - 1]&lt;br /&gt;
     A.heapSize = A.heapSize - 1&lt;br /&gt;
     siftDown(0)&lt;br /&gt;
     '''return''' min&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Добавление нового элемента===&lt;br /&gt;
&lt;br /&gt;
Выполняет добавление элемента в кучу за время &amp;lt;tex&amp;gt;O(\log{N})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Добавление произвольного элемента в конец кучи, и восстановление свойства упорядоченности с помощью процедуры &amp;lt;math&amp;gt; \mathrm {siftUp} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 '''function''' insert(key : '''int'''):&lt;br /&gt;
     A.heapSize = A.heapSize + 1&lt;br /&gt;
     A[A.heapSize - 1] = key&lt;br /&gt;
     siftUp(A.heapSize - 1)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Построение кучи за O(N) ==&lt;br /&gt;
{{Определение | definition =&lt;br /&gt;
'''&amp;lt;tex&amp;gt;D&amp;lt;/tex&amp;gt;-куча''' {{---}} это куча, в которой у каждого элемента, кроме, возможно, элементов на последнем уровне, ровно &amp;lt;tex&amp;gt;D&amp;lt;/tex&amp;gt; потомков. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Дан массив &amp;lt;tex&amp;gt;A[0.. N - 1].&amp;lt;/tex&amp;gt; Требуется построить &amp;lt;tex&amp;gt;D&amp;lt;/tex&amp;gt;-кучу с минимумом в корне. Наиболее очевидный способ построить такую кучу из неупорядоченного массива {{---}} сделать нулевой элемент массива корнем, а дальше по очереди добавить все его элементы в конец кучи и запускать от каждого добавленного элемента &amp;lt;math&amp;gt;\mathrm {siftUp}&amp;lt;/math&amp;gt;. Временная оценка такого алгоритма &amp;lt;tex&amp;gt; O(N\log{N})&amp;lt;/tex&amp;gt;. Однако можно построить кучу еще быстрее — за &amp;lt;tex&amp;gt; O(N) &amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Представим, что в массиве хранится дерево (&amp;lt;tex&amp;gt;A[0] - &amp;lt;/tex&amp;gt;  корень, а потомками элемента &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; являются &amp;lt;tex&amp;gt;A[2i+1]...A[2i+D]&amp;lt;/tex&amp;gt;). Сделаем &amp;lt;tex&amp;gt; \mathrm {siftDown} &amp;lt;/tex&amp;gt; для вершин, имеющих хотя бы одного потомка: от &amp;lt;tex dpi=140&amp;gt;\genfrac {}{}{}{}{N}{D}&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;,{{---}} так как поддеревья, состоящие из одной вершины без потомков, уже упорядочены.&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= На выходе получим искомую кучу. &lt;br /&gt;
|proof= При вызове &amp;lt;tex&amp;gt; \mathrm {siftDown} &amp;lt;/tex&amp;gt; для вершины, ее поддеревья являются кучами. После выполнения &amp;lt;tex&amp;gt; \mathrm {siftDown} &amp;lt;/tex&amp;gt; эта вершина с ее поддеревьями будут также являться кучей.  Значит, после выполнения всех &amp;lt;tex&amp;gt; \mathrm {siftDown} &amp;lt;/tex&amp;gt; получится куча.&lt;br /&gt;
}}&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Время работы этого алгоритма &amp;lt;tex&amp;gt; O(N) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof= Число вершин на высоте &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt; в куче из &amp;lt;tex&amp;gt;N&amp;lt;/tex&amp;gt; элементов не превосходит &amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt;  \left \lceil \frac{N}{D^h} \right \rceil &amp;lt;/tex&amp;gt;.  Высота кучи не превосходит &amp;lt;tex&amp;gt; \log_{D}N &amp;lt;/tex&amp;gt;. Обозначим за &amp;lt;tex&amp;gt; H &amp;lt;/tex&amp;gt; высоту дерева, тогда время построения не превосходит&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt; \sum_{h = 1}^H  \limits\frac{N}{D^h} \cdot D &amp;lt;/tex&amp;gt; &amp;lt;tex dpi = &amp;quot;150&amp;quot;&amp;gt;  \cdot  h &amp;lt;/tex&amp;gt; &amp;lt;tex  dpi = &amp;quot;160&amp;quot;&amp;gt; = N \cdot D \cdot {\sum_{h = 1}^H  \limits}\frac{h}{D^h}. &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Докажем вспомогательную лемму о сумме ряда.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= &amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt; {\sum_{h = 1}^\infty  \limits}\frac{h}{D^h} = \frac{D}{(D - 1)^2} . &amp;lt;/tex&amp;gt; &lt;br /&gt;
|proof=&lt;br /&gt;
 Обозначим за &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; сумму ряда. Заметим, что&lt;br /&gt;
&amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt; \frac{n}{D^n} = \frac{1}{D} \cdot \frac{n - 1}{D ^{n - 1}} + \frac{1}{D^n}. &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt;{\sum_{n = 1}^\infty  \limits}\frac{1}{d^n}&amp;lt;/tex&amp;gt; {{---}} это сумма бесконечной убывающей геометрической прогрессии, и она равна &amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt; &lt;br /&gt;
\frac{\frac{1}{D}}{1 - \frac{1}{D}} = \frac{1}{D - 1}. &amp;lt;/tex&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Получаем  &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; &amp;lt;tex dpi = &amp;quot;160&amp;quot; &amp;gt;=\frac{1}{D}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\cdot S +&amp;lt;/tex&amp;gt;  &amp;lt;tex dpi = &amp;quot;160&amp;quot; &amp;gt; \frac{1}{D - 1}. &amp;lt;/tex&amp;gt; Откуда &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; &amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt;  = \frac{D}{(D - 1)^2}. &amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Подставляя в нашу формулу результат леммы, получаем &amp;lt;tex &amp;gt;N&amp;lt;/tex&amp;gt; &amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt;\cdot (\frac {D}{D - 1})^2 &amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;  \leqslant  4 \cdot N &amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;=O(N).&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Биномиальная куча]]&lt;br /&gt;
* [[Фибоначчиева куча]]&lt;br /&gt;
* [[Левосторонняя куча]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [[wikipedia:ru:Двоичная куча|Википедия {{---}} Двоичная куча]]&lt;br /&gt;
* [[wikipedia:ru:Очередь с приоритетом|Википедия {{---}} Очередь с приоритетом]]&lt;br /&gt;
* [[wikipedia:en:Binary heap|Wikipedia {{---}} Binary heap]]&lt;br /&gt;
* [[wikipedia:en:Priority queue|Wikipedia {{---}} Priority queue]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Приоритетные очереди]]&lt;/div&gt;</summary>
		<author><name>188.162.65.63</name></author>	</entry>

	</feed>