<?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=217.66.156.147&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=217.66.156.147&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/217.66.156.147"/>
		<updated>2026-05-01T18:46:49Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9B%D0%B5%D0%B2%D0%BE%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D1%8F%D1%8F_%D0%BA%D1%83%D1%87%D0%B0&amp;diff=45215</id>
		<title>Левосторонняя куча</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9B%D0%B5%D0%B2%D0%BE%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D1%8F%D1%8F_%D0%BA%D1%83%D1%87%D0%B0&amp;diff=45215"/>
				<updated>2015-03-30T18:43:15Z</updated>
		
		<summary type="html">&lt;p&gt;217.66.156.147: /* Условие кучи */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Файл:LeftistHeap.jpg|400px|thumb|right|Левосторонняя куча]]&lt;br /&gt;
==Условие кучи==&lt;br /&gt;
Левосторонние деревья были изобретены Кларком Крейном (Clark Allan Crane), свое название они получили из-за того, что левое поддерево обычно длиннее правого.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition='''Левосторонняя куча''' (англ. ''leftist heap'') {{---}} двоичное левосторонее [[Дерево, эквивалентные определения|дерево]] (не обязательно сбалансированное), но с соблюдением [[Двоичная куча#Определение|порядка кучи]] (heap order).}}&lt;br /&gt;
'''Свободной позицией''' назовем место в дереве, куда может быть вставлена новая вершина. Само дерево будет являться свободной позицией, если оно не содержит вершин. Если же у какой-то внутренней вершины нет сына, то на его месте {{---}} ''свободная позиция''.&lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=lemma1&lt;br /&gt;
|about=1&lt;br /&gt;
|statement=В двоичном дереве с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершинами существует свободная позиция на глубине не более &amp;lt;tex&amp;gt;\log{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=Если бы все свободные позиции были на глубине более логарифма, то мы получили бы полное дерево с количеством вершин более &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;. }}&lt;br /&gt;
&lt;br /&gt;
Левосторонняя куча накладывает на двоичное дерево дополнительное условие.&lt;br /&gt;
Ближайшая свободная позиция должна быть самой правой позицией в дереве. То есть помимо обычного условия кучи выполняется следующее:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition='''Условие левосторонней кучи'''. Пусть &amp;lt;tex&amp;gt;dist(u)&amp;lt;/tex&amp;gt; {{---}} расстояние от вершины &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; до ближайшей свободной позиции в ее поддереве. У пустых позиций &amp;lt;tex&amp;gt;dist = 0&amp;lt;/tex&amp;gt;. Тогда потребуем для любой вершины &amp;lt;tex&amp;gt;u : dist(u.left)\geqslant dist(u.right)&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
Если для какой- то вершины это свойство не выполняется, то это легко устраняется: можно за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; поменять местами левого и правого ребенка, что не повлияет на порядок кучи.&lt;br /&gt;
&lt;br /&gt;
==Поддерживаемые операции==&lt;br /&gt;
===merge===&lt;br /&gt;
Слияние двух куч. &lt;br /&gt;
  &lt;br /&gt;
  '''LeftistHeap''' merge(x, y): &amp;lt;font color=darkgreen&amp;gt;// x, y {{---}} корни двух деревьев&amp;lt;/font&amp;gt;&lt;br /&gt;
    '''if''' x == &amp;lt;tex&amp;gt; \varnothing &amp;lt;/tex&amp;gt;: &lt;br /&gt;
        '''return''' y&lt;br /&gt;
    '''if''' y == &amp;lt;tex&amp;gt; \varnothing &amp;lt;/tex&amp;gt;: &lt;br /&gt;
        '''return''' x&lt;br /&gt;
    '''if''' y.key &amp;lt; x.key:&lt;br /&gt;
        swap(x, y)&lt;br /&gt;
    &amp;lt;font color=darkgreen&amp;gt;// Воспользуемся тем, что куча левосторонняя. Правая ветка {{---}} самая короткая и не длиннее логарифма.&lt;br /&gt;
    // Пойдем направо и сольем правое поддерево с у.&amp;lt;/font&amp;gt;&lt;br /&gt;
    x.right = '''merge'''(x.right, y)&lt;br /&gt;
    &amp;lt;font color=darkgreen&amp;gt;// Могло возникнуть  нарушение левостороннести кучи&amp;lt;/font&amp;gt;&lt;br /&gt;
    '''if''' dist(x.right) &amp;gt; dist(x.left):&lt;br /&gt;
        swap(x.left, x.right)&lt;br /&gt;
    dist(x) = min(dist(x.left), dist(x.right)) + 1 &amp;lt;font color=darkgreen&amp;gt;// пересчитаем расстояние до ближайшей свободной позиции&amp;lt;/font&amp;gt;&lt;br /&gt;
    '''return''' x&lt;br /&gt;
    &amp;lt;font color=darkgreen&amp;gt;// Каждый раз идем из уже существующей вершины только в правое поддерево {{---}} не более логарифма вызовов (по лемме)&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Левосторонняя куча относится к сливаемым кучам: остальные операции легко реализуются с помощью операции слияния.&lt;br /&gt;
&lt;br /&gt;
===insert===&lt;br /&gt;
Вставка новой вершины в дерево. Новое левостороннее дерево, состоящее из одной вершины, сливается с исходным.&lt;br /&gt;
===extractMin===&lt;br /&gt;
Как и у любой другой двоичной кучи, минимум хранится в корне. Извлекаем минимальное значение, удаляем корень, сливаем левое и правое поддерево корня. Возвращает пару из извлеченной вершины и новой кучи.&lt;br /&gt;
===delete===&lt;br /&gt;
Аналогично удаляется любой элемент {{---}} на его место ставится результат слияния его детей. Но так просто любой элемент удалить нельзя {{---}} на пути от этого элемента к корню может нарушиться левостороннесть кучи. А до корня мы дойти не можем, так как элемент может находиться на линейной глубине. Поэтому удаление реализуется с помощью &amp;lt;tex&amp;gt;\mathrm{decreaseKey}&amp;lt;/tex&amp;gt;. Уменьшаем ключ до &amp;lt;tex&amp;gt;-\infty&amp;lt;/tex&amp;gt;, затем извлекаем минимальное значение. &lt;br /&gt;
===decreaseKey===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=lemma2&lt;br /&gt;
|about=2&lt;br /&gt;
|statement=У левостороннего дерева с правой ветвью длинны &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt; количество узлов &amp;lt;tex&amp;gt;n \geqslant 2^{h} - 1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=Индукция по h.&lt;br /&gt;
&lt;br /&gt;
При &amp;lt;tex&amp;gt;h = 1&amp;lt;/tex&amp;gt; {{---}} верно.&lt;br /&gt;
&lt;br /&gt;
При &amp;lt;tex&amp;gt;h &amp;gt; 1&amp;lt;/tex&amp;gt; левое и правое поддеревья исходного дерева левосторонние, а &amp;lt;tex&amp;gt;dist&amp;lt;/tex&amp;gt; от их корней больше либо равен &amp;lt;tex&amp;gt;h - 1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
По индукции число узлов в каждом из них больше или равно &amp;lt;tex&amp;gt;2^{h - 1} - 1&amp;lt;/tex&amp;gt;, тогда во всем дереве &amp;lt;tex&amp;gt;n \geqslant (2^{h - 1} - 1) + (2^{h - 1} - 1) + 1 = 2^{h} - 1&amp;lt;/tex&amp;gt; узлов.}}&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
* Найдем узел &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, вырежем поддерево с корнем в этом узле.&lt;br /&gt;
* Пройдем от предка вырезанной вершины, при этом пересчитывая &amp;lt;tex&amp;gt;dist&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;dist&amp;lt;/tex&amp;gt; левого сына вершины меньше &amp;lt;tex&amp;gt;dist&amp;lt;/tex&amp;gt; правого, то меняем местами поддеревья.&lt;br /&gt;
* Уменьшаем ключ данного узла и сливаем два дерева: исходное и вырезанное. &lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=lemma3&lt;br /&gt;
|about=3&lt;br /&gt;
|statement= Нужно транспонировать не более &amp;lt;tex&amp;gt;\log{n}&amp;lt;/tex&amp;gt; поддеревьев. &lt;br /&gt;
|proof=Длина пути от вершины до корня может быть и &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;, но нам не нужно подниматься до корня {{---}} достаточно подняться до вершины, у которой свойство левосторонней кучи уже выполнено. Транспонируем только если &amp;lt;tex&amp;gt;dist(x.left) &amp;lt; dist(x.right)&amp;lt;/tex&amp;gt;, но &amp;lt;tex&amp;gt;dist(x.right) \leqslant \log{n}&amp;lt;/tex&amp;gt;. На каждом шаге, если нужно транспонируем и увеличиваем &amp;lt;tex&amp;gt;dist&amp;lt;/tex&amp;gt;, тогда  &amp;lt;tex&amp;gt;dist&amp;lt;/tex&amp;gt; увеличится до &amp;lt;tex&amp;gt;\log{n}&amp;lt;/tex&amp;gt; и обменов уже не надо будет делать.}}&lt;br /&gt;
Таким образом, мы восстановили левостороннесть кучи за &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;. Поэтому асимптотика операции &amp;lt;tex&amp;gt;\mathrm{decreaseKey} &amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Построение кучи за O(n)==&lt;br /&gt;
Храним список левосторонних куч. Пока их количество больше &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, из начала списка достаем две кучи, сливаем их и кладем в конец списка.&lt;br /&gt;
&lt;br /&gt;
Покажем, почему это будет работать за &amp;lt;tex&amp;gt; O(n) &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; 2^i &amp;lt;/tex&amp;gt;. На нулевом шаге у нас &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; куч из одного элемента, и на каждом следующем количество куч будет уменьшаться вдвое, а число вершин в куче будет увеличиваться вдвое. Слияние двух куч из &amp;lt;tex&amp;gt; n_i &amp;lt;/tex&amp;gt; элементов выполняется за &amp;lt;tex&amp;gt; O(\log{n_i}) &amp;lt;/tex&amp;gt;. Поэтому построение будет выполняться за&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex dpi = 130&amp;gt; &lt;br /&gt;
\sum\limits_{i = 1}^{\left\lceil \log{n} \right\rceil} \genfrac{}{}{}{0}{n \cdot \log{n_i}}{2^i} = &lt;br /&gt;
n \cdot \sum\limits_{i = 1}^{\left\lceil \log{n} \right\rceil} \genfrac{}{}{}{0}{\log{2^i}}{2^i} = &lt;br /&gt;
n \cdot \sum\limits_{i = 1}^{\left\lceil \log{n} \right\rceil} \genfrac{}{}{}{0}{i}{2^i}&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Покажем, что сумма {{---}} &amp;lt;tex&amp;gt; O(1) &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 dpi = 130&amp;gt; &lt;br /&gt;
\sum\limits_{i = 1}^{\left\lceil \log{n} \right\rceil} \genfrac{}{}{}{0}{i}{2^i} &amp;lt; \sum\limits_{i = 1}^{\infty } \genfrac{}{}{}{0}{i}{2^i} \\&lt;br /&gt;
f(x) = \sum\limits_{i = 1}^{\infty } \Bigl. i \cdot x^i \Bigr|_{x = \frac{1}{2}} \\ &lt;br /&gt;
~\genfrac{}{}{}{0}{f(x)}{x} = \sum\limits_{i = 1}^{\infty } i \cdot x^{i - 1} = \sum\limits_{i = 1}^{\infty } (x^i)' = (\sum\limits_{i = 1}^{\infty } x^i)' \\&lt;br /&gt;
~\int\genfrac{}{}{}{0}{f(x)}{x} = \sum\limits_{i = 1}^{\infty } x^i =~\genfrac{}{}{}{0}{1}{1 - x} - 1 \\&lt;br /&gt;
~\genfrac{}{}{}{0}{f(x)}{x} = (\genfrac{}{}{}{0}{1}{1 - x} - 1)' = \genfrac{}{}{}{0}{1}{(1 - x)^2} \\&lt;br /&gt;
~f(x) = \genfrac{}{}{}{0}{x}{(1 - x)^2}&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После подстановки &amp;lt;tex&amp;gt; x = \genfrac{}{}{}{0}{1}{2} &amp;lt;/tex&amp;gt; получаем, что сумма равна &amp;lt;tex&amp;gt; 2 &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;\mathrm{merge}&amp;lt;/tex&amp;gt;. Эта реализация слияния является функциональной — ее легко реализовать на функциональном языке программирования. Также данная реализация &amp;lt;tex&amp;gt;\mathrm{merge}&amp;lt;/tex&amp;gt; является персистентной.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
* [http://compscicenter.ru/program/lecture/6829 Лекция &amp;quot;Приоритетные очереди&amp;quot; А. С. Станкевича в Computer Science Center]&lt;br /&gt;
* [http://www.intuit.ru/studies/courses/100/100/lecture/1539?page=1 Левосторонние кучи. Интуит.]&lt;br /&gt;
* [[wikipedia:Leftist_tree|Wikipedia {{---}} Leftist tree]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Приоритетные очереди]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>217.66.156.147</name></author>	</entry>

	</feed>