<?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=178.71.136.166&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=178.71.136.166&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/178.71.136.166"/>
		<updated>2026-05-13T18:28:04Z</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=30716</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=30716"/>
				<updated>2013-05-20T19:09:22Z</updated>
		
		<summary type="html">&lt;p&gt;178.71.136.166: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Определение==&lt;br /&gt;
Левосторонние деревья были изобретены Кларком Крейном (Clark Allan Crane), свое название они получили из-за того, что левое поддерево обычно длиннее правого.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition='''Левосторонняя куча (leftist heap)''' — двоичное левосторонее дерево (не обязательно сбалансированное), но с соблюдением порядка кучи (heap order).}}&lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=lemma1&lt;br /&gt;
|about=1&lt;br /&gt;
|statement=В двоичном дереве с n вершинами существует свободная позиция на глубине не более logn.&lt;br /&gt;
|proof=Если бы все свободные позиции были на глубине более логарифма, то мы получили бы полное дерево с количеством вершин более n. }}&lt;br /&gt;
&lt;br /&gt;
Левосторонняя куча накладывает на двоичное дерево дополнительное условие.&lt;br /&gt;
Ближайшая свободная позиция должна быть самой правой позицией в дереве. То есть помимо условия кучи выполняется следующее:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=Условие левосторонней кучи. Пусть dist(х) – расстояние от вершины u до ближайшей свободной позиции в ее поддереве. У пустых позиций dist = 0. Тогда потребуем для любой вершины х: dist(x.L)&amp;gt;= dict(x.R).}}&lt;br /&gt;
&lt;br /&gt;
Если для какой- то вершины это свойство не выполняется, то это легко устраняется: можно за О(1) поменять местами левого и правого ребенка, что не повлияет на порядок кучи.&lt;br /&gt;
&lt;br /&gt;
==Поддерживаемые операции==&lt;br /&gt;
===merge===&lt;br /&gt;
Слияние двух куч.&lt;br /&gt;
&lt;br /&gt;
  merge(x,y) //x,y – корни двух деревьев&lt;br /&gt;
    if x == NULL return y&lt;br /&gt;
    if y == NULL return x&lt;br /&gt;
    if y.key &amp;lt; x.key :&lt;br /&gt;
      x&amp;lt;-&amp;gt;y&lt;br /&gt;
    //Воспользуемся тем, что куча левосторонняя. Правая ветка — самая короткая и не длиннее&lt;br /&gt;
    //логарифма. Пойдем направо и сольем правое поддерево с у.&lt;br /&gt;
    x.R&amp;lt;-merge(x.R, y)&lt;br /&gt;
    //Могло возникнуть  нарушение левостороннести кучи.&lt;br /&gt;
    If dist(x.R) &amp;gt; dist(x.L):&lt;br /&gt;
      x.L&amp;lt;-&amp;gt;x.R&lt;br /&gt;
    update dist(x) //dist(x) = min(dist(x.L), dist(x.R)) + 1&lt;br /&gt;
    return x;&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;
Аналогично удаляется любой элемент — на его место ставится результат слияния его детей. Но так просто любой элемент удалить нельзя — на пути от этого элемента к корню может нарушиться левостороннесть кучи. А до корня мы дойти не можем, так как элемент может находиться на линейной глубине. Поэтому удаление реализуется с помощью decrease key. Уменьшаем ключ до -inf, затем извлекаем минимальное значение. &lt;br /&gt;
===decKey===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=lemma2&lt;br /&gt;
|about=2&lt;br /&gt;
|statement=У левостороннего дерева с правой ветвью длинны h количество узлов n&amp;gt;= 2^h – 1.&lt;br /&gt;
|proof=Индукция по h.&lt;br /&gt;
&lt;br /&gt;
При h = 1 – верно.&lt;br /&gt;
&lt;br /&gt;
При h &amp;gt; 1 левое и правое поддеревья исходного дерева левосторонние, а dist от их корней &amp;gt;= h – 1.&lt;br /&gt;
&lt;br /&gt;
По индукции число узлов в каждом из них &amp;gt;=2^(h - 1) – 1, тогда во все дереве n &amp;gt;= (2^(h – 1) – 1) + (2^(h – 1) – 1) +1 = 2^h – 1 узлов.}}&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
1. Найдем узел х, вырежем поддерево с корнем в этом узле.&lt;br /&gt;
&lt;br /&gt;
2. Пройдем от предка вырезанной вершины, при этом пересчитывая dist. Если dist левого сына вершины меньше  dist правого, то меняем местами поддеревья.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=lemma3&lt;br /&gt;
|about=3&lt;br /&gt;
|statement= Нужно транспонировать не более logn поддеревьев. &lt;br /&gt;
|proof=Длина пути от вершины до корня может быть и O(n), но нам не нужно подниматься до корня — достаточно подняться до вершины, у которой свойство левостороннести уже выполнено. Транспонируем только если dist(x.L) &amp;lt; dist(x.R), но dist(x.R) &amp;lt;= logn. На каждом шаге, если нужно транспонируем и увеличиваем dist++, тогда  dist увеличится до logn и обменов уже не надо будет делать.}}&lt;br /&gt;
Таким образом, мы восстановили левостороннесть кучи за O(logn)&lt;br /&gt;
&lt;br /&gt;
3. Уменьшаем ключ данного узла и сливаем два дерева: исходное и вырезанное. O(logn)&lt;br /&gt;
==Построение кучи за О(n)==&lt;br /&gt;
Храним список левосторонних куч. Пока их количество больше &amp;gt;1, из начала списка достаем две кучи, сливаем их и кладем в конец списка. &lt;br /&gt;
==Преимущества левосторонней кучи==&lt;br /&gt;
Нигде не делается уничтожающих присваиваний. Не создается новых узлов в merge. Эта реализация слияния является функциональной — ее легко реализовать на функциональном языке программирования. Также данная реалзация merge является персистентной.&lt;/div&gt;</summary>
		<author><name>178.71.136.166</name></author>	</entry>

	<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=30715</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=30715"/>
				<updated>2013-05-20T19:03:32Z</updated>
		
		<summary type="html">&lt;p&gt;178.71.136.166: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Определение==&lt;br /&gt;
Левосторонние деревья были изобретены Кларком Крейном (Clark Allan Crane), свое название они получили из-за того, что левое поддерево обычно длиннее правого.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition='''Левосторонняя куча (leftist heap)''' — двоичное левосторонее дерево (не обязательно сбалансированное), но с соблюдением порядка кучи (heap order).}}&lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=lemma1&lt;br /&gt;
|about=1&lt;br /&gt;
|statement=В двоичном дереве с n вершинами существует свободная позиция на глубине не более logn.&lt;br /&gt;
|proof=Если бы все свободные позиции были на глубине более логарифма, то мы получили бы полное дерево с количеством вершин более n. }}&lt;br /&gt;
&lt;br /&gt;
Левосторонняя куча накладывает на двоичное дерево дополнительное условие.&lt;br /&gt;
Ближайшая свободная позиция должна быть самой правой позицией в дереве. То есть помимо условия кучи выполняется следующее:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=Условие левосторонней кучи. Пусть dist(х) – расстояние от вершины u до ближайшей свободной позиции в ее поддереве. У пустых позиций dist = 0. Тогда потребуем для любой вершины х: dist(x.L)&amp;gt;= dict(x.R).}}&lt;br /&gt;
&lt;br /&gt;
Если для какой- то вершины это свойство не выполняется, то это легко устраняется: можно за О(1) поменять местами левого и правого ребенка, что не повлияет на порядок кучи.&lt;br /&gt;
&lt;br /&gt;
==Поддерживаемые операции==&lt;br /&gt;
===merge===&lt;br /&gt;
Слияние двух куч.&lt;br /&gt;
&lt;br /&gt;
  merge(x,y) //x,y – корни двух деревьев&lt;br /&gt;
    if x == NULL return y&lt;br /&gt;
    if y == NULL return x&lt;br /&gt;
    if y.key &amp;lt; x.key :&lt;br /&gt;
      x&amp;lt;-&amp;gt;y&lt;br /&gt;
    //Воспользуемся тем, что куча левосторонняя. Правая ветка — самая короткая и не длиннее&lt;br /&gt;
    //логарифма. Пойдем направо и сольем правое поддерево с у.&lt;br /&gt;
    x.R&amp;lt;-merge(x.R, y)&lt;br /&gt;
    //Могло возникнуть  нарушение левостороннести кучи.&lt;br /&gt;
    If dist(x.R) &amp;gt; dist(x.L):&lt;br /&gt;
      x.L&amp;lt;-&amp;gt;x.R&lt;br /&gt;
    update dist(x) //dist(x) = min(dist(x.L), dist(x.R)) + 1&lt;br /&gt;
    return x;&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;
Аналогично удаляется любой элемент — на его место ставится результат слияния его детей. Но так просто любой элемент удалить нельзя — на пути от этого элемента к корню может нарушиться левостороннесть кучи. А до корня мы дойти не можем, так как элемент может находиться на линейной глубине. Поэтому удаление реализуется с помощью decrease key. Уменьшаем ключ до -inf, затем извлекаем минимальное значение. &lt;br /&gt;
===decKey===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=lemma2&lt;br /&gt;
|about=2&lt;br /&gt;
|statement=У левостороннего дерева с правой ветвью длинны h количество узлов n&amp;gt;= 2^h – 1.&lt;br /&gt;
|proof=Индукция по h.&lt;br /&gt;
При h = 1 – верно.&lt;br /&gt;
При h &amp;gt; 1 левое и правое поддеревья исходного дерева левосторонние, а dist от их корней &amp;gt;= h – 1. По индукции число узлов в каждом из них &amp;gt;=2^(h - 1) – 1, тогда во все дереве n &amp;gt;= (2^(h – 1) – 1) + (2^(h – 1) – 1) +1 = 2^h – 1 узлов.}}&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
1. Найдем узел х, вырежем поддерево с корнем в этом узле.&lt;br /&gt;
2. Пройдем от предка вырезанной вершины, при этом пересчитывая dist. Если dist левого сына вершины меньше  dist правого, то меняем местами поддеревья.&lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=lemma3&lt;br /&gt;
|about=3&lt;br /&gt;
|statement= Нужно транспонировать не более logn поддеревьев. &lt;br /&gt;
|proof=Длина пути от вершины до корня может быть и O(n), но нам не нужно подниматься до корня — достаточно подняться до вершины, у которой свойство левостороннести уже выполнено. Транспонируем только если dist(x.L) &amp;lt; dist(x.R), но dist(x.R) &amp;lt;= logn. На каждом шаге, если нужно транспонируем и увеличиваем dist++, тогда  dist увеличится до logn и обменов уже не надо будет делать.}}&lt;br /&gt;
Таким образом, мы восстановили левостороннесть кучи за O(logn)&lt;br /&gt;
3. Уменьшаем ключ данного узла и сливаем два дерева: исходное и вырезанное. O(logn)&lt;br /&gt;
==Построение кучи за О(n)==&lt;br /&gt;
Храним список левосторонних куч. Пока их количество больше &amp;gt;1, из начала списка достаем две кучи, сливаем их и кладем в конец списка. &lt;br /&gt;
==Преимущества левосторонней кучи==&lt;br /&gt;
Нигде не делается уничтожающих присваиваний. Не создается новых узлов в merge. Эта реализация слияния является функциональной — ее легко реализовать на функциональном языке программирования. Также данная реалзация merge является персистентной.&lt;/div&gt;</summary>
		<author><name>178.71.136.166</name></author>	</entry>

	<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=30713</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=30713"/>
				<updated>2013-05-20T18:55:09Z</updated>
		
		<summary type="html">&lt;p&gt;178.71.136.166: Новая страница: «==Определение== Левосторонние деревья были изобретены Кларком Крейном (Clark Allan Crane), свое н...»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Определение==&lt;br /&gt;
Левосторонние деревья были изобретены Кларком Крейном (Clark Allan Crane), свое название они получили из-за того, что левое поддерево обычно длиннее правого.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition='''Левосторонняя куча (leftist heap)''' — двоичное левосторонее дерево (не обязательно сбалансированное), но с соблюдением порядка кучи (heap order).}}&lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=lemma1&lt;br /&gt;
|about=1&lt;br /&gt;
|statement=В двоичном дереве с n вершинами существует свободная позиция на глубине не более logn.&lt;br /&gt;
|proof=Если бы все свободные позиции были на глубине более логарифма, то мы получили бы полное дерево с количеством вершин более n. }}&lt;br /&gt;
&lt;br /&gt;
Левосторонняя куча накладывает на двоичное дерево дополнительное условие.&lt;br /&gt;
Ближайшая свободная позиция должна быть самой правой позицией в дереве. То есть помимо условия кучи выполняется следующее:&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=Условие левосторонней кучи. Пусть dist(х) – расстояние от вершины u до ближайшей свободной позиции в ее поддереве. У пустых позиций dist = 0. Тогда потребуем для любой вершины х: dist(x.L)&amp;gt;= dict(x.R).}}&lt;br /&gt;
&lt;br /&gt;
Если для какой- то вершины это свойство не выполняется, то это легко устраняется: можно за О(1) поменять местами левого и правого ребенка, что не повлияет на порядок кучи.&lt;br /&gt;
&lt;br /&gt;
==Поддерживаемые операции==&lt;br /&gt;
===merge===&lt;br /&gt;
Слияние двух куч.&lt;br /&gt;
&lt;br /&gt;
merge(x,y) //x,y – корни двух деревьев&lt;br /&gt;
if x == NULL return y&lt;br /&gt;
if y == NULL return x&lt;br /&gt;
if y.key &amp;lt; x.key :&lt;br /&gt;
    x&amp;lt;-&amp;gt; y&lt;br /&gt;
//Воспользуемся тем, что куча левосторонняя. Правая ветка — самая короткая и не длиннее&lt;br /&gt;
//логарифма. Пойдем направо и сольем правое поддерево с у.&lt;br /&gt;
x.R&amp;lt;-merge(x.R, y)&lt;br /&gt;
//Могло возникнуть  нарушение левостороннести кучи.&lt;br /&gt;
If dist(x.R) &amp;gt; dist(x.L): x.L&amp;lt;-&amp;gt;x.R&lt;br /&gt;
update dist(x) //dist(x) = min(dist(x.L), dist(x.R)) + 1&lt;br /&gt;
return x;&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;
Аналогично удаляется любой элемент — на его место ставится результат слияния его детей. Но так просто любой элемент удалить нельзя — на пути от этого элемента к корню может нарушиться левостороннесть кучи. А до корня мы дойти не можем, так как элемент может находиться на линейной глубине. Поэтому удаление реализуется с помощью decrease key. Уменьшаем ключ до -inf, затем извлекаем минимальное значение. &lt;br /&gt;
===decKey===&lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=lemma2&lt;br /&gt;
|about=2&lt;br /&gt;
|statement=У левостороннего дерева с правой ветвью длинны h количество узлов n&amp;gt;= 2^h – 1.&lt;br /&gt;
|proof=Индукция по h.&lt;br /&gt;
При h = 1 – верно.&lt;br /&gt;
При h &amp;gt; 1 левое и правое поддеревья исходного дерева левосторонние, а dist от их корней &amp;gt;= h – 1. По индукции число узлов в каждом из них &amp;gt;=2^(h - 1) – 1, тогда во все дереве n &amp;gt;= (2^(h – 1) – 1) + (2^(h – 1) – 1) +1 = 2^h – 1 узлов.}}&lt;br /&gt;
====Алгоритм====&lt;br /&gt;
1. Найдем узел х, вырежем поддерево с корнем в этом узле.&lt;br /&gt;
2. Пройдем от предка вырезанной вершины, при этом пересчитывая dist. Если dist левого сына вершины меньше  dist правого, то меняем местами поддеревья.&lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=lemma3&lt;br /&gt;
|about=3&lt;br /&gt;
|statement= Нужно транспонировать не более logn поддеревьев. &lt;br /&gt;
|proof=Длина пути от вершины до корня может быть и O(n), но нам не нужно подниматься до корня — достаточно подняться до вершины, у которой свойство левостороннести уже выполнено. Транспонируем только если dist(x.L) &amp;lt; dist(x.R), но dist(x.R) &amp;lt;= logn. На каждом шаге, если нужно транспонируем и увеличиваем dist++, тогда  dist увеличится до logn и обменов уже не надо будет делать.}}&lt;br /&gt;
Таким образом, мы восстановили левостороннесть кучи за O(logn)&lt;br /&gt;
3. Уменьшаем ключ данного узла и сливаем два дерева: исходное и вырезанное. O(logn)&lt;br /&gt;
==Построение кучи за О(n)==&lt;br /&gt;
Храним список левосторонних куч. Пока их количество больше &amp;gt;1, из начала списка достаем две кучи, сливаем их и кладем в конец списка. &lt;br /&gt;
==Преимущества левосторонней кучи==&lt;br /&gt;
Нигде не делается уничтожающих присваиваний. Не создается новых узлов в merge. Эта реализация слияния является функциональной — ее легко реализовать на функциональном языке программирования. Также данная реалзация merge является персистентной.&lt;/div&gt;</summary>
		<author><name>178.71.136.166</name></author>	</entry>

	</feed>