<?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=212.102.57.138&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=212.102.57.138&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/212.102.57.138"/>
		<updated>2026-04-15T01:25: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=74966</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=74966"/>
				<updated>2020-08-01T22:42:07Z</updated>
		
		<summary type="html">&lt;p&gt;212.102.57.138: /* Извлечение минимального элемента */ форматирование&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Определение==&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Двоичная куча''' или '''пирамида''' (англ. ''Binary heap'') — такое двоичное [[Дерево, эквивалентные определения|подвешенное дерево]], для которого выполнены следующие три условия:&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;
[[Файл:Min_heap.png‎|thumb|325px|Пример кучи для минимума]]&lt;br /&gt;
[[Файл:Min_heap_array.png‎|thumb|325px|Хранение кучи в массиве, красная стрелка {{---}} левый сын, зеленая {{---}} правый]]&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;
&lt;br /&gt;
====siftDown====&lt;br /&gt;
Если значение измененного элемента увеличивается, то свойства кучи восстанавливаются функцией &amp;lt;tex&amp;gt; \mathrm {siftDown} &amp;lt;/tex&amp;gt;.&lt;br /&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;
&lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' siftDown(i : '''int'''):&lt;br /&gt;
     '''while''' 2 * i + 1 &amp;lt; 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; a.heapSize '''and''' a[right] &amp;lt; a[left]&lt;br /&gt;
             j = right&lt;br /&gt;
         '''if''' a[i] &amp;lt;= 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 style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' siftUp(i : '''int'''):&lt;br /&gt;
     '''while''' a[i] &amp;lt; 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;
 '''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;
&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 style=&amp;quot;display:inline-block&amp;quot;&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[di+1]...a[di+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;\dfrac{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;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' buldHeap():&lt;br /&gt;
     '''for''' i = a.heapSize / 2 '''downto''' 0&lt;br /&gt;
         siftDown(i)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Слияние двух куч===&lt;br /&gt;
Даны две кучи &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, размерами &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt;, требуется объединить эти две кучи. &lt;br /&gt;
====Наивная реализация====&lt;br /&gt;
Поочередно добавим все элементы из &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. Время работы {{---}} &amp;lt;tex&amp;gt;O(m \log(n+m))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' merge(a, b : '''Heap'''):&lt;br /&gt;
     '''while''' b.heapSize &amp;gt; 0  &lt;br /&gt;
         a.insert(b.extractMin())&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Реализация с помощью построения кучи====&lt;br /&gt;
Добавим все элементы кучи &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt; в конец массива &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, после чего вызовем функцию построения кучи. Процедура выполняется за время &amp;lt;tex&amp;gt;O(n + m)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' merge(a, b : '''Heap'''):&lt;br /&gt;
     '''for''' i = 0 '''to''' b.heapSize - 1  &lt;br /&gt;
         a.heapSize = a.heapSize + 1&lt;br /&gt;
         a[a.heapSize - 1] = b[i]&lt;br /&gt;
     a.heapify()&lt;br /&gt;
&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
===Поиск k-ого элемента (очень коряво расписано с неверными индексами)===&lt;br /&gt;
Требуется найти &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-ый по величине элемент в куче.&lt;br /&gt;
&lt;br /&gt;
# Создаем новую кучу, в которой будем хранить пару &amp;lt;tex&amp;gt;\langle \mathtt{value}, \mathtt{index} \rangle&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathtt{value}&amp;lt;/tex&amp;gt; {{---}} значение элемента, а &amp;lt;tex&amp;gt;\mathtt{index}&amp;lt;/tex&amp;gt; {{---}} индекс элемента в основном массиве, и добавляем в нее корень кучи. &lt;br /&gt;
# Возьмем корень новой кучи и добавим её детей из основной кучи, после чего удалим корень. Проделаем этот шаг &amp;lt;tex&amp;gt;k - 1&amp;lt;/tex&amp;gt; раз.&lt;br /&gt;
# В корне новой кучи будет находиться ответ.&lt;br /&gt;
&lt;br /&gt;
Время работы алгоритма {{---}} &amp;lt;tex&amp;gt;O(k \log k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При &amp;lt;tex&amp;gt;n \lessapprox k \log k &amp;lt;/tex&amp;gt; выгоднее запускать [[поиск k-ой порядковой статистики]].&lt;br /&gt;
[[Файл:Min_heap_kth.png‎|thumb|center|650px|Пример при &amp;lt;tex&amp;gt;k = 5&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;
* [[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;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>212.102.57.138</name></author>	</entry>

	</feed>