<?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=92.242.59.6&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=92.242.59.6&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/92.242.59.6"/>
		<updated>2026-06-11T14:09:21Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B8%D0%B1%D0%BE%D0%BD%D0%B0%D1%87%D1%87%D0%B8%D0%B5%D0%B2%D0%B0_%D0%BA%D1%83%D1%87%D0%B0&amp;diff=67217</id>
		<title>Фибоначчиева куча</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B8%D0%B1%D0%BE%D0%BD%D0%B0%D1%87%D1%87%D0%B8%D0%B5%D0%B2%D0%B0_%D0%BA%D1%83%D1%87%D0%B0&amp;diff=67217"/>
				<updated>2018-11-28T08:32:09Z</updated>
		
		<summary type="html">&lt;p&gt;92.242.59.6: /* Реализация */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Фибоначчиева куча''' (англ. ''Fibonacci heap'')  {{---}} структура данных, отвечающая интерфейсу [[Приоритетные очереди#Операции | приоритетная очередь]]. Эта структура данных имеет меньшую [[Амортизационный анализ#Основные определения | амортизированную  сложность]], чем такие приоритетные очереди как [[Биномиальная куча | биномиальная куча]] и [[Двоичная куча | двоичная куча]]. Изначально эта структура данных была разработана Майклом Фридманом&amp;lt;ref&amp;gt;[[wikipedia:en:Michael_Fredman | Майкл Фридман {{---}} Википедия]]&amp;lt;/ref&amp;gt; и Робертом Тарьяном&amp;lt;ref&amp;gt;[[wikipedia:en:Robert_Tarjan | Роберт Тарьян {{---}} Википедия]]&amp;lt;/ref&amp;gt; при работе по улучшению асимптотической сложности [[Алгоритм Дейкстры | алгоритма Дейкстры]]. Свое название Фибоначчиева куча получила  из-за использования некоторых свойств  чисел Фибоначчи&amp;lt;ref&amp;gt;[[wikipedia:en:Fibonacci_number | Числа Фибоначчи {{---}} Википедия]]&amp;lt;/ref&amp;gt; в [[Амортизационный анализ#Метод потенциалов | потенциальном анализе]] этой реализации.&lt;br /&gt;
== Структура ==&lt;br /&gt;
Фибоначчиева куча  {{---}} набор из [[Дерево, эквивалентные определения | подвешенных деревьев]] удовлетворяющих свойству: каждый предок не больше своих детей(если дерево на минимум). Это означает, что минимум всей кучи это один из корней этих деревьев. Одним из главных преимуществ Фибоначчиевой кучи {{---}} гибкость её структуры из-за того, что на деревья не наложены никакие ограничения по форме. Например, Фибоначчиева куча может состоять хоть из деревьев в каждом из которых по одному элементу. Такая гибкость позволяет выполнять некоторые операции лениво, оставляя работу более поздним операциям. Далее будут даны некоторые определения, которые понадобятся в дальнейшем.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Степень вершины''' (англ. ''degree'')  {{---}} количество детей данной вершины. Далее будем обозначать как &amp;lt;tex&amp;gt;degree(x)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; это вершина.&lt;br /&gt;
}}&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Степень кучи''' (англ. ''degree'')  {{---}} наибольшая степень вершины этой кучи. Далее будем обозначать как &amp;lt;tex&amp;gt;degree(H)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; это куча.&lt;br /&gt;
}}&lt;br /&gt;
== Реализация == &lt;br /&gt;
[[File:Fibonacci-heap.png|thumb|340px|Пример фибоначчиевой кучи]]&lt;br /&gt;
Для возможности быстрого удаления элемента из произвольного места и объединением с другим списком будем хранить их в [[Список#Циклический список | циклическом двусвязном списке]]. Также будем хранить и все уровни поддерева. Исходя из этого структура каждого узла будет выглядеть вот так.&lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''struct''' Node&lt;br /&gt;
    '''int''' key &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;     // ключ&amp;lt;/span&amp;gt;&lt;br /&gt;
    '''Node''' parent &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt; // указатель на родительский узел&amp;lt;/span&amp;gt;&lt;br /&gt;
    '''Node''' child &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;  // указатель на один из дочерних узлов&amp;lt;/span&amp;gt;&lt;br /&gt;
    '''Node''' left &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;   // указатель на левый узел того же предка&amp;lt;/span&amp;gt;&lt;br /&gt;
    '''Node''' right &amp;lt;span style=&amp;quot;color:#009000&amp;quot;&amp;gt;  // указатель на правый узел того же предка&amp;lt;/span&amp;gt;&lt;br /&gt;
    '''int''' degree &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;  // степень вершины&amp;lt;/span&amp;gt;&lt;br /&gt;
    '''boolean''' mark &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;// был ли удален в процессе изменения ключа ребенок этой вершины)&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;Также стоит упомянуть, что нам нужен указатель только на одного ребенка, поскольку остальные хранятся в двусвязном списке с ним. Для доступа ко всей куче нам тоже нужен всего один элемент, поэтому разумно хранить именно указатель на минимум кучи (он обязательно один из корней), а для получения размера за константное время будем хранить размер кучи отдельно.&lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''struct''' fibonacciHeap&lt;br /&gt;
    '''int''' size &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;// текущее количество узлов&amp;lt;/span&amp;gt;&lt;br /&gt;
    '''Node''' min &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;// указатель на корень дерева с минимальным ключом&amp;lt;/span&amp;gt; &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
==== Cоздание кучи ====&lt;br /&gt;
Инициализация кучи.&lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' buildHeap:&lt;br /&gt;
    min &amp;lt;tex&amp;gt;= \varnothing&amp;lt;/tex&amp;gt; &lt;br /&gt;
    size = 0&lt;br /&gt;
&amp;lt;/code&amp;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''' insert(x: '''int'''):&lt;br /&gt;
    '''Node''' newNode &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;               // создаем новый узел&amp;lt;/span&amp;gt; &lt;br /&gt;
    newNode.key = x &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;            // инициализируем ключ нового узла&amp;lt;/span&amp;gt;&lt;br /&gt;
    '''if''' size = 0 &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;               // если куче нет элементов, то только что добавленный минимальный&amp;lt;/span&amp;gt;&lt;br /&gt;
        min = newNode&lt;br /&gt;
        min.left = newNode&lt;br /&gt;
        min.right = newNode&lt;br /&gt;
    '''else'''&amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;                        // иначе аккуратно меняем указатели в списке, чтобы не перепутать указатели&amp;lt;/span&amp;gt;&lt;br /&gt;
        '''Node''' prevRight = min.right &lt;br /&gt;
        min.right = newNode&lt;br /&gt;
        newNode.left = min &lt;br /&gt;
        newNode.right = prevRight &lt;br /&gt;
        prevRight.left = newNode &lt;br /&gt;
    '''if''' newNode.key &amp;lt; min.key&lt;br /&gt;
        min = newNode &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;          // меняем указатель на минимум, если надо&amp;lt;/span&amp;gt;&lt;br /&gt;
    newNode.parent &amp;lt;tex&amp;gt;= \varnothing&amp;lt;/tex&amp;gt; &lt;br /&gt;
    size++ &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;                     // не забываем увеличить переменную size &amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;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;
 '''int''' getMin:&lt;br /&gt;
    '''return''' min.key&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
==== Соедининение двух куч ====&lt;br /&gt;
Для сливание двух Фибоначчиевых куч необходимо просто объединить их корневые списки, а также обновить минимум новой кучи, если понадобится. Вынесем в вспомогательную функцию &amp;lt;tex&amp;gt;unionLists&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''' unionLists(first: '''Node''', second: '''Node'''):&lt;br /&gt;
    '''Node''' L = first.left &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;              // аккуратно меняем указатели местами указатели&amp;lt;/span&amp;gt;&lt;br /&gt;
    '''Node''' R = second.right&lt;br /&gt;
    second.right = first&lt;br /&gt;
    first.left = second&lt;br /&gt;
    L.right = R&lt;br /&gt;
    R.left = L&lt;br /&gt;
&amp;lt;/code&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(that: '''fibonacciHeap'''):&lt;br /&gt;
    '''if''' that.size = 0 &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;              // если вторая куча пуста, нечего добавлять&amp;lt;/span&amp;gt;&lt;br /&gt;
        '''return'''&lt;br /&gt;
    '''if''' size = 0 &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;                   // если наша куча пуста, то результатом будет вторая куча&amp;lt;/span&amp;gt;&lt;br /&gt;
        min = that.min&lt;br /&gt;
        size = that.size&lt;br /&gt;
    '''else''' &lt;br /&gt;
        unionLists(min, that.min) &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;  // объединяем два корневых списка&amp;lt;/span&amp;gt;&lt;br /&gt;
        size += that.size&lt;br /&gt;
    '''if''' min &amp;lt;tex&amp;gt;= \varnothing&amp;lt;/tex&amp;gt; '''or''' (that.min &amp;lt;tex&amp;gt; \neq \varnothing&amp;lt;/tex&amp;gt; '''and''' that.min &amp;lt; min) &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;// если минимум кучи изменился, то надо обновить указатель&amp;lt;/span&amp;gt;&lt;br /&gt;
        min = that.min&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
==== Удаление минимального элемента====&lt;br /&gt;
Первая рассматриваемая операция, в ходе которой значительно меняется структура кучи. Здесь используется вспомогательная процедура &amp;lt;tex&amp;gt;consolidate&amp;lt;/tex&amp;gt;, благодаря которой собственно и достигается желанная амортизированная оценка. В данном случае &amp;lt;tex&amp;gt; min = \varnothing&amp;lt;/tex&amp;gt; не рассматривается и считается нарушением предусловий &amp;lt;tex&amp;gt;deleteMin&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''int''' deleteMin:&lt;br /&gt;
    '''Node''' prevMin = min &lt;br /&gt;
    unionLists(min, min.child) &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;   // список детей min объединяем с корневым&amp;lt;/span&amp;gt;&lt;br /&gt;
    '''Node''' L = min.left &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;            // аккуратно удаляем min из списка&amp;lt;/span&amp;gt;&lt;br /&gt;
    '''Node''' R = min.right&lt;br /&gt;
    L.right = R&lt;br /&gt;
    R.left = L&lt;br /&gt;
    '''if''' prevMin.right = prevMin &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;  // отдельно рассмотрим случай с одним элементом&amp;lt;/span&amp;gt;&lt;br /&gt;
        min &amp;lt;tex&amp;gt;= \varnothing&amp;lt;/tex&amp;gt;&lt;br /&gt;
        '''return'''&lt;br /&gt;
    min = min.right &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;              // пока что перекинем указаиель min на правого сына, а далее consolidate() скорректирует min в процессе выполнения&amp;lt;/span&amp;gt;&lt;br /&gt;
    consolidate()&lt;br /&gt;
    size-- &lt;br /&gt;
    '''return''' prevMin.key&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
===== Прорежение деревьев =====&lt;br /&gt;
Данная процедура принимает кучу и преобразует ее таким образом, что в корневом списке остается не более &amp;lt;tex&amp;gt; degree(H) + 1&amp;lt;/tex&amp;gt; вершин.&lt;br /&gt;
&lt;br /&gt;
Для этого возьмем массив списков указателей на корни деревьев &amp;lt;tex&amp;gt; A[0 \dots D[H]] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; degree(H) &amp;lt;/tex&amp;gt; {{---}} максимальная степень вершины в текущем корневом списке.&lt;br /&gt;
&lt;br /&gt;
Затем происходит [[Биномиальная_куча#merge | процесс, аналогичный слиянию биномиальных куч]]: добавляем поочередно каждый корень, смотря на его степень. Пусть она равна &amp;lt;tex&amp;gt; d &amp;lt;/tex&amp;gt;. Если в соответствующей ячейке &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; еще нету вершины, записываем текущую вершину туда. Иначе подвешиваем одно дерево к другому, и пытаемся также добавить дерево, степень корня которого уже равна &amp;lt;tex&amp;gt; d + 1 &amp;lt;/tex&amp;gt;. Продолжаем, пока не найдем свободную ячейку. Подвешиваем мы его следующим образом: в корневой список добавляем корень минимальный из тех двух, а корень другого добавляем в список детей корневой вершины. Чтобы лучше понять этот процесс лучше воспользоваться [https://www.cs.usfca.edu/~galles/visualization/FibonacciHeap.html визуализатором]&lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' consolidate:&lt;br /&gt;
    A = '''Node[]'''&lt;br /&gt;
    A[min.degree] = min &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;                   // создаем массив и инициализируем его min&amp;lt;/span&amp;gt;&lt;br /&gt;
    '''Node''' current = min.right&lt;br /&gt;
    '''while''' A[current.degree] &amp;lt;tex&amp;gt;\neq&amp;lt;/tex&amp;gt; current&amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;     // пока элементы массива меняются&amp;lt;/span&amp;gt;&lt;br /&gt;
        '''if''' A[current.degree] &amp;lt;tex&amp;gt;= \varnothing&amp;lt;/tex&amp;gt; &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;        // если ячейка пустая, то положим в нее текущий элемент&amp;lt;/span&amp;gt;&lt;br /&gt;
            A[current.degree] = current&lt;br /&gt;
            current = current.right&lt;br /&gt;
        '''else''' &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;                              // иначе подвесим к меньшему из текущего корня и того, который лежит в ячейке другой&amp;lt;/span&amp;gt;&lt;br /&gt;
            '''Node''' conflict = A[current.degree]&lt;br /&gt;
            '''Node''' addTo, adding&lt;br /&gt;
            '''if''' conflict.key &amp;lt; current.key&lt;br /&gt;
                addTo = conflict&lt;br /&gt;
                adding = current&lt;br /&gt;
            '''else'''&lt;br /&gt;
                addTo = current&lt;br /&gt;
                adding = conflict&lt;br /&gt;
            unionLists(addTo.child, adding)&lt;br /&gt;
            adding.parent = addTo&lt;br /&gt;
            addTo.degree++&lt;br /&gt;
            current = addTo&lt;br /&gt;
        '''if''' min.key &amp;gt; current.key &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;         // обновляем минимум, если нужно&amp;lt;/span&amp;gt;&lt;br /&gt;
            min = current     &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
'''Пример'''&lt;br /&gt;
&lt;br /&gt;
Изначально добавляем в нашу кучу &amp;lt;tex&amp;gt;7&amp;lt;/tex&amp;gt; элементов &amp;lt;tex&amp;gt;56, 22, 84, 32, 85, 15, 16&amp;lt;/tex&amp;gt;. После этого выполним операцию извлечения минимума:&lt;br /&gt;
&lt;br /&gt;
[[File:Fibonacci-heap-consolidate-example-1.png|thumb|center|500px|Начальное состояние кучи]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Удалим минимальный элемент из циклического корневого списка и заведем массив &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; для дальнейшего прорежения.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Fibonacci-heap-consolidate-example-2.png|thumb|center|500px|Удаление мимимума и создание массива]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Начнем процесс протяжения с первого элемента {{---}} &amp;lt;tex&amp;gt;56&amp;lt;/tex&amp;gt;. Его степень равна &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; поэтому запишем его адрес в нулевую ячейку массива.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Fibonacci-heap-consolidate-example-3.png|thumb|center|500px|Состояние массива после первой итерации]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Следующий элемент &amp;lt;tex&amp;gt;22&amp;lt;/tex&amp;gt; тоже имеет степень &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;. Возникает конфликт, который решается подвешиванием к меньшему корню большего. То есть к &amp;lt;tex&amp;gt;22&amp;lt;/tex&amp;gt; подвешиваем &amp;lt;tex&amp;gt;56&amp;lt;/tex&amp;gt; и увеличиваем степень &amp;lt;tex&amp;gt;22&amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. В итоге степень &amp;lt;tex&amp;gt;22&amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Записываем адрес &amp;lt;tex&amp;gt;22&amp;lt;/tex&amp;gt; по индексу &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; в массив.&lt;br /&gt;
&lt;br /&gt;
[[File:Fibonacci-heap-consolidate-example-4.png.png|thumb|center|500px|Состояние после второй итерации]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Делаем тоже самое, что и на предыдущих итерациях, но теперь объединяем &amp;lt;tex&amp;gt;32&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;84&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Fibonacci-heap-consolidate-example-5.png|thumb|center|500px|Состояние после четвертой итерации]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Теперь у нас два элемента со степенью &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; в корневом списке. Объединим их подвесив к меньшему корню {{---}} &amp;lt;tex&amp;gt;22&amp;lt;/tex&amp;gt;, больший {{---}} &amp;lt;tex&amp;gt;32&amp;lt;/tex&amp;gt;. Теперь степень &amp;lt;tex&amp;gt;22&amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt;, запишем на &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt; позицию массива обновленное значение.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Fibonacci-heap-consolidate-example-6.png|thumb|center|500px|Состояние после пятой итерации]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Ну и наконец аналогично объедений последние два элемента.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Fibonacci-heap-consolidate-example-7.png|thumb|center|500px|Финальное состояние кучи]]&lt;br /&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;
# Иначе, вырезаем дерево с текущей вершиной в корневой [[Список |список]], и производим каскадное вырезание родителя. &lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' decreaseKey(x: '''Node''', newValue: '''int'''):&lt;br /&gt;
    '''if''' newValue &amp;gt; x.parent.key &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt; // если после изменения структура дерева сохранится, то меняем и выходим&amp;lt;/span&amp;gt;&lt;br /&gt;
        x.key = newValue&lt;br /&gt;
        '''return'''&lt;br /&gt;
    '''Node''' parent = x.parent &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;    // иначе вызываем cut и cascadingCut&amp;lt;/span&amp;gt;&lt;br /&gt;
    cut(x)&lt;br /&gt;
    cascadingCut(x.parent)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
===== Вырезание =====&lt;br /&gt;
При вырезании вершины мы удаляем ее из списка детей своего родителя, уменьшаем степень ее родителя (&amp;lt;tex&amp;gt; x.p.degree &amp;lt;/tex&amp;gt;) и снимаем пометку с текущей вершины (&amp;lt;tex&amp;gt; x.mark = false &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''' cut(x: '''Node''')&lt;br /&gt;
    '''Node''' L = x.left&lt;br /&gt;
    '''Node''' R = x.right&lt;br /&gt;
    R.left = L &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;               // аккуратно удаляем текущую вершину&amp;lt;/span&amp;gt;&lt;br /&gt;
    L.right = R&lt;br /&gt;
    x.right = x&lt;br /&gt;
    x.left = x&lt;br /&gt;
    x.parent.degree--&lt;br /&gt;
    '''if''' x.parent.child = x &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;  // чтобы родитель не потерял ссылку на сыновей проверяем: &amp;lt;/span&amp;gt;&lt;br /&gt;
        '''if''' x.right = x. &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;    // если узел который мы вырезаем содержится в родителе, то меняем его на соседний&amp;lt;/span&amp;gt;&lt;br /&gt;
            x.parent.child &amp;lt;tex&amp;gt;= \varnothing&amp;lt;/tex&amp;gt; &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt; // иначе у родителя больше нет детей&amp;lt;/span&amp;gt;&lt;br /&gt;
        '''else'''&lt;br /&gt;
            x.parent.child = x.right&lt;br /&gt;
    x.parent &amp;lt;tex&amp;gt;= \varnothing&amp;lt;/tex&amp;gt;&lt;br /&gt;
    unionLists(min, x) &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;       // вставляем наше поддерево в корневой список&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Каскадное вырезание =====&lt;br /&gt;
Перед вызовом каскадного вырезания нам известно, удаляли ли ребенка у этой вершины. Если у вершины до этого не удаляли дочерний узел (&amp;lt;tex&amp;gt; x.mark = false &amp;lt;/tex&amp;gt;), то мы помечаем эту вершину (&amp;lt;tex&amp;gt; x.mark = true &amp;lt;/tex&amp;gt;) и прекращаем выполнение операции. В противном случае применяем операцию &amp;lt;tex&amp;gt;\mathrm {cut}&amp;lt;/tex&amp;gt; для текущей вершины и запускаем каскадное вырезание от родителя.&lt;br /&gt;
[[File:Каскадное вырезание.png|thumb|500px|Пример каскадного вырезания]]&lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' cascadingCut(x: '''Node''')&lt;br /&gt;
    '''while''' x.mark = '''true''' &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt; // пока у нас помеченые вершины вырезаем их&amp;lt;/span&amp;gt;&lt;br /&gt;
        cut(x)&lt;br /&gt;
        x = x.parent&lt;br /&gt;
    x.mark = true &amp;lt;span style=&amp;quot;color:#008000&amp;quot;&amp;gt;        // последнюю вершину нужно пометить {{---}} у нее удаляли ребенка&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/code&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;3&amp;lt;/tex&amp;gt; фибоначчиевых деревьев. У вершины с ключом &amp;lt;tex&amp;gt;24&amp;lt;/tex&amp;gt; отсутствует &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; ребенок.&lt;br /&gt;
* Уменьшаем ключ &amp;lt;tex&amp;gt;26&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;5&amp;lt;/tex&amp;gt; и делаем операцию &amp;lt;tex&amp;gt;\mathrm {cut}&amp;lt;/tex&amp;gt; этого дерева. Получаем кучу с &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt; деревьями и новым минимумом. Но у вершины с ключом &amp;lt;tex&amp;gt;24&amp;lt;/tex&amp;gt; был удален второй ребенок, поэтому запускам операцию &amp;lt;tex&amp;gt;\mathrm {cascadingCut}&amp;lt;/tex&amp;gt; для этой вершины: вырезаем ее, помещаем в корневой [[Список |список]] и помечаем ее родителя.&lt;br /&gt;
* У вершины с ключом &amp;lt;tex&amp;gt;7&amp;lt;/tex&amp;gt; удален лишь один ребенок, поэтому операция &amp;lt;tex&amp;gt;\mathrm {cascadingCut}&amp;lt;/tex&amp;gt; от нее не запускается. В итоге, получаем кучу, состоящую из &amp;lt;tex&amp;gt;5&amp;lt;/tex&amp;gt; фибоначчиевых деревьев.&lt;br /&gt;
&lt;br /&gt;
==== Удаление элемента ====&lt;br /&gt;
Удаление вершины реализуется через уменьшение ее ключа до &amp;lt;tex&amp;gt; -\infty &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''' delete(x: '''Node''')&lt;br /&gt;
    decreaseKey(x, &amp;lt;tex&amp;gt;-\infty&amp;lt;/tex&amp;gt;)&lt;br /&gt;
    deleteMin()&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; \Phi = trees + 2 * marked &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; trees &amp;lt;/tex&amp;gt; {{---}} количество элементов в корневом списке кучи, а &amp;lt;tex&amp;gt; marked &amp;lt;/tex&amp;gt; {{---}} количество вершин, у которых удален один ребенок (то есть вершин с пометкой &amp;lt;tex&amp;gt; x.mark = true &amp;lt;/tex&amp;gt;). Договоримся, что единицы потенциала достаточно для оплаты константного количества работы.&lt;br /&gt;
==== Cоздание кучи ====&lt;br /&gt;
Очевидно, что реальное время работы {{---}} &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
==== Вставка элемента ====&lt;br /&gt;
Для оценки амортизированной стоимости операции рассмотрим исходную кучу &amp;lt;tex&amp;gt; H &amp;lt;/tex&amp;gt; и получившуюся в результате вставки нового элемента кучу &amp;lt;tex&amp;gt; H' &amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt; trees(H') = trees(H) + 1 &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; marked(H') = marked(H) &amp;lt;/tex&amp;gt;. Следовательно, увеличение потенциала составляет &amp;lt;tex&amp;gt; (trees(H) + 1 + 2 * marked(H)) - (trees(H) + 2 * marked(H)) = 1 &amp;lt;/tex&amp;gt;. Так как реальное время работы составляет &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, то амортизированная стоимость данной операции также равна &amp;lt;tex&amp;gt; O(1) &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;
Реальное время работы {{---}} &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;. Амортизированное время работы также &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, поскольку, при объединении двух куч в одну, потенциалы обеих куч суммируются, итоговая сумма потенциалов не изменяется, &amp;lt;tex&amp;gt; \Phi_{n + 1} - \Phi_n = 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
==== Удаление минимального элемента====&lt;br /&gt;
Для доказательства времени работы этого алгоритма нам понадобится доказать несколько вспомогательных утверждений.&lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=Лемма1&lt;br /&gt;
|statement=Для всех целых &amp;lt;tex&amp;gt; n \geqslant 2&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; F_n = 1 + \sum\limits_{i=0}^{n-2} F_i &amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt; F_n &amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;-ое число Фибоначчи, определяемое формулой:&lt;br /&gt;
&amp;lt;tex&amp;gt;&lt;br /&gt;
F_n =&lt;br /&gt;
\begin{cases}&lt;br /&gt;
 0, &amp;amp; n = 0 \\&lt;br /&gt;
 1, &amp;amp; n = 1 \\&lt;br /&gt;
 F_{n-1} + F_{n-2}, &amp;amp; n \geqslant 2&lt;br /&gt;
\end{cases} &amp;lt;/tex&amp;gt;&lt;br /&gt;
|proof=&lt;br /&gt;
Докажем лемму по индукции:&lt;br /&gt;
&lt;br /&gt;
при &amp;lt;tex&amp;gt;n = 2&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_2 = 1 + \sum\limits_{i=0}^0 F_i = 1 + 0 = 1&amp;lt;/tex&amp;gt;, что действительно верно.&lt;br /&gt;
&lt;br /&gt;
По индукции предполагаем, что &amp;lt;tex&amp;gt;F_{n-1} = 1 + \sum\limits_{i=0}^{n-3} F_i &amp;lt;/tex&amp;gt;. Тогда&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_n = F_{n-1} + F_{n-2} = 1 + \sum\limits_{i=0}^{n-3} F_i + F_{n-2} = 1 + \sum\limits_{i=0}^{n-2} F_i&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=Лемма2&lt;br /&gt;
|statement= Фибоначчиево дерево порядка &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; содержит не менее &amp;lt;tex&amp;gt;F_n&amp;lt;/tex&amp;gt; вершин.&lt;br /&gt;
|proof=&lt;br /&gt;
Докажем это утверждение по индукции.&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;s_n&amp;lt;/tex&amp;gt; {{---}} минимальный размер фибоначчиева дерева порядка &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При &amp;lt;tex&amp;gt;n = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;s_0 = 1 &amp;gt; F_0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При &amp;lt;tex&amp;gt;n = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;s_1 = 1 = F_1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Предположим по индукции, что для всех &amp;lt;tex&amp;gt;i &amp;lt; n \ s_i \geqslant F_i&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Пусть в нашем дереве удалено поддерево порядка &amp;lt;tex&amp;gt;n - 1&amp;lt;/tex&amp;gt;. Тогда&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;s_n = 1 + \sum\limits_{i=0}^{n-2} s_i \geqslant 1 + \sum\limits_{i=0}^{n-2} F_i&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Но по предыдущей [[#Лемма1|лемме]] :&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;1 + \sum\limits_{i=0}^{n-2} F_i = F_n&amp;lt;/tex&amp;gt;. Следовательно, &amp;lt;tex&amp;gt;s_n \geqslant F_n&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=Лемма3&lt;br /&gt;
|statement= &amp;lt;tex&amp;gt;F_n =O(\varphi^n)&amp;lt;/tex&amp;gt;, где &amp;lt;tex dpi=&amp;quot;160&amp;quot;&amp;gt; \varphi = \frac {1 + \sqrt 5} {2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|proof=&lt;br /&gt;
Для начала докажем, что &amp;lt;tex&amp;gt;F_n =&amp;lt;/tex&amp;gt; &amp;lt;tex dpi=&amp;quot;160&amp;quot;&amp;gt;\frac {\varphi^n - (-\varphi)^{-n}} {\sqrt 5}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Используем для этого математическую индукцию.&lt;br /&gt;
&lt;br /&gt;
При &amp;lt;tex&amp;gt;n = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_0 =&amp;lt;/tex&amp;gt; &amp;lt;tex dpi=&amp;quot;160&amp;quot;&amp;gt;\frac {\varphi^0 - (-\varphi)^0} {\sqrt 5} = \frac {1 - 1} {\sqrt 5} = 0&amp;lt;/tex&amp;gt;, что верно.&lt;br /&gt;
&lt;br /&gt;
При &amp;lt;tex&amp;gt;n = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_1 =&amp;lt;/tex&amp;gt; &amp;lt;tex dpi=&amp;quot;160&amp;quot;&amp;gt;\frac {\varphi^1 - (-\varphi)^{-1}} {\sqrt 5} = \frac {1} {\sqrt 5}(\frac {1 + \sqrt 5} {2} - \frac {1 - \sqrt 5} {2}) = \frac {2\sqrt 5} {2\sqrt 5} = 1&amp;lt;/tex&amp;gt;, что также верно.&lt;br /&gt;
&lt;br /&gt;
По индукции предполагаем, что &amp;lt;tex&amp;gt;F_{n-1} =&amp;lt;/tex&amp;gt; &amp;lt;tex dpi=&amp;quot;160&amp;quot;&amp;gt;\frac {\varphi^{n-1} - (-\varphi)^{1-n}} {\sqrt 5}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;F_{n-2} =&amp;lt;/tex&amp;gt; &amp;lt;tex dpi=&amp;quot;160&amp;quot;&amp;gt;\frac {\varphi^{n-2} - (-\varphi)^{2-n}} {\sqrt 5}&amp;lt;/tex&amp;gt;. Тогда&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_n = F_{n-1} + F_{n-2} =&amp;lt;/tex&amp;gt; &amp;lt;tex dpi=&amp;quot;160&amp;quot;&amp;gt;\frac {\varphi^{n-1} - (-\varphi)^{1-n}} {\sqrt 5} + \frac {\varphi^{n-2} - (-\varphi)^{2-n}} {\sqrt 5} =&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex dpi=&amp;quot;160&amp;quot;&amp;gt;= \frac {1} {\sqrt 5}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;(\varphi^{n-1} - (-\varphi)^{1-n} + \varphi^{n-2} - (-\varphi)^{2-n}) &amp;lt;/tex&amp;gt; &amp;lt;tex dpi=&amp;quot;160&amp;quot;&amp;gt;= \frac {1} {\sqrt 5}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;(\varphi^{n}(\varphi^{-1} + \varphi^{-2}) - (-\varphi)^{-n}(-\varphi + \varphi^{2}))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подставив вместо &amp;lt;tex&amp;gt;\varphi&amp;lt;/tex&amp;gt; его значение, нетрудно убедится, что &amp;lt;tex&amp;gt;\varphi^{-1} + \varphi^{-2} = -\varphi + \varphi^{2} = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку &amp;lt;tex&amp;gt;\left\vert (-\varphi)^{-1} \right\vert &amp;lt; 1&amp;lt;/tex&amp;gt;, то выполняются неравенства &amp;lt;tex dpi=&amp;quot;160&amp;quot;&amp;gt;\frac {(-\varphi)^{-n}} {\sqrt 5} &amp;lt; \frac {1} {\sqrt 5} &amp;lt; \frac {1} {2}&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;\frac {\varphi^{n}} {\sqrt 5}&amp;lt;/tex&amp;gt;, округленному до ближайшего целого числа. Следовательно, &amp;lt;tex&amp;gt;F_n =O(\varphi^n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|id=Лемма4&lt;br /&gt;
|statement=Максимальная степень &amp;lt;tex&amp;gt;degree&amp;lt;/tex&amp;gt; произвольной вершины в фибоначчиевой куче с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершинами равна &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
|proof=&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; {{---}} произвольная вершина в фибоначчиевой куче с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершинами, и пусть &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; {{---}} степень вершины &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Тогда по [[#Лемма2|доказанному выше]] в дереве, корень которого &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, содержится не менее &amp;lt;tex&amp;gt;F_k&amp;lt;/tex&amp;gt; вершин, что в свою очередь по [[#Лемма3|лемме]] равно &amp;lt;tex&amp;gt;O(\varphi^k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
То есть&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n \geqslant \varphi^{k}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Логарифмируя по основанию &amp;lt;tex&amp;gt;\varphi&amp;lt;/tex&amp;gt;, получаем&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\log_{\varphi}n \geqslant k&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, максимальная степень &amp;lt;tex&amp;gt;degree&amp;lt;/tex&amp;gt; произвольной вершины равна &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Итоговая асимптотика операции &amp;lt;tex&amp;gt;\mathrm {extraxtMin}&amp;lt;/tex&amp;gt;, учитывая и вспомогательную функцию &amp;lt;tex&amp;gt; \mathrm {consolidate} &amp;lt;/tex&amp;gt;, время работы которой доказывается ниже, равно: &amp;lt;tex&amp;gt; O(1)+O(degree)+O(degree)=O(degree) &amp;lt;/tex&amp;gt;. По доказанной выше [[#Лемма4|лемме]] &amp;lt;tex&amp;gt;O(degree) = O(\log(n))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Учетная стоимость &amp;lt;tex&amp;gt; \mathrm {consolidate} &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; O(degree) &amp;lt;/tex&amp;gt;. Докажем это:&lt;br /&gt;
&lt;br /&gt;
Изначально в корневом списке было не более &amp;lt;tex&amp;gt; degree + trees - 1 &amp;lt;/tex&amp;gt; вершин, поскольку он состоит из исходного списка корней с &amp;lt;tex&amp;gt;trees&amp;lt;/tex&amp;gt; узлами, минус извлеченный узел и плюс дочерние узлы, количество которых не превышает &amp;lt;tex&amp;gt; degree &amp;lt;/tex&amp;gt;. В ходе операции &amp;lt;tex&amp;gt; \mathrm {consolidate} &amp;lt;/tex&amp;gt; мы сделали &amp;lt;tex&amp;gt; O(degree + trees) &amp;lt;/tex&amp;gt; слияний деревьев. Потенциал перед извлечением минимума равен &amp;lt;tex&amp;gt; trees + 2 * marked &amp;lt;/tex&amp;gt;, а после не превышает &amp;lt;tex&amp;gt; degree + 1 + 2 * marked&amp;lt;/tex&amp;gt;, поскольку в корневом списке остается не более  &amp;lt;tex&amp;gt; degree + 1 &amp;lt;/tex&amp;gt; узлов, а количество помеченных узлов не изменяется. Таким образом, амортизированная стоимость не превосходит&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; O(degree + trees) + (degree + 1 + 2 * marked) - (trees + 2 * marked) = O(degree) + O(trees) - trees&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку мы договорились, что можем масштабировать единицу потенциала таким образом, чтобы покрывать константное количество работы, то итоговая амортизационная оценка {{---}} &amp;lt;tex&amp;gt; O(degree) &amp;lt;/tex&amp;gt;&lt;br /&gt;
==== Уменьшение значения элемента ====&lt;br /&gt;
Докажем, что амортизированное время работы операции &amp;lt;tex&amp;gt; \mathrm {decreaseKey} &amp;lt;/tex&amp;gt; есть &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;. Поскольку в процедуре нет циклов, ее время работы определяется лишь количеством рекурсивных вызовов каскадного вырезания.&lt;br /&gt;
&lt;br /&gt;
Пусть мы вызвали процедуру каскадного вырезания подверглось &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; раз. Так как реальное время работы каждой итерации &amp;lt;tex&amp;gt; \mathrm {cascadingCut} &amp;lt;/tex&amp;gt; составляет &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, то реальное время работы операции &amp;lt;tex&amp;gt; \mathrm {decreaseKey} &amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt; O(k) &amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Рассмотрим, как изменится потенциал в результате выполнения данной операции. Пусть &amp;lt;tex&amp;gt; H &amp;lt;/tex&amp;gt; {{---}} фибоначчиева куча до вызова &amp;lt;tex&amp;gt; \mathrm {decreaseKey} &amp;lt;/tex&amp;gt;. Тогда после &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; итераций операции &amp;lt;tex&amp;gt; \mathrm {cascadingCut} &amp;lt;/tex&amp;gt; вершин с пометкой &amp;lt;tex&amp;gt; x.mark = true &amp;lt;/tex&amp;gt; стало как минимум на &amp;lt;tex&amp;gt; k - 2 &amp;lt;/tex&amp;gt; меньше, потому что каждый вызов каскадного вырезания, за исключением последнего, уменьшает количество помеченных вершин на одну, и в результате последнего вызова одну вершину мы можем пометить. В корневом списке прибавилось &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; новых деревьев (&amp;lt;tex&amp;gt; k - 1 &amp;lt;/tex&amp;gt; дерево за счет каскадного вырезания и еще одно из-за самого первого вызова операции &amp;lt;tex&amp;gt; \mathrm {cut} &amp;lt;/tex&amp;gt;). &lt;br /&gt;
&lt;br /&gt;
В итоге, изменение потенциала составляет: &amp;lt;tex&amp;gt; \Phi_i - \Phi_{i - 1} = ((trees + k) + 2 * (marked + k - 2)) - (trees + 2 * marked) = 4 - k &amp;lt;/tex&amp;gt;. Следовательно, амортизированная стоимость не превышает &amp;lt;tex&amp;gt; O(k) + 4 - k &amp;lt;/tex&amp;gt;. Но поскольку мы можем соответствующим образом масштабировать единицы потенциала, то амортизированная стоимость операции &amp;lt;tex&amp;gt; \mathrm {decreaseKey} &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
==== Удаление элемента ====&lt;br /&gt;
Амортизированное время работы: &amp;lt;tex&amp;gt; O(1) + O(degree) = O(degree) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Поскольку ранее мы показали, что &amp;lt;tex&amp;gt; degree = O(\log n ) &amp;lt;/tex&amp;gt;, то соответствующие оценки доказаны.&lt;br /&gt;
==== Итоговая таблица ====&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Операция&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Амортизированная сложность&lt;br /&gt;
|-align=&amp;quot;center&amp;quot;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;\mathrm {makeHeap}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; &lt;br /&gt;
|-align=&amp;quot;center&amp;quot;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;\mathrm {insert}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; &lt;br /&gt;
|-align=&amp;quot;center&amp;quot;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;\mathrm {getMin}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; &lt;br /&gt;
|-align=&amp;quot;center&amp;quot;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;\mathrm {merge}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; &lt;br /&gt;
|-align=&amp;quot;center&amp;quot;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;\mathrm {extractMin}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;O(\log n )&amp;lt;/tex&amp;gt; &lt;br /&gt;
|-align=&amp;quot;center&amp;quot;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;\mathrm {decreaseKey}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; &lt;br /&gt;
|-align=&amp;quot;center&amp;quot;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &amp;lt;tex&amp;gt;\mathrm {delete}&amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| &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;
* Большое потребление памяти на узел(минимум 21 байт)&lt;br /&gt;
* Большая константа времени работы, что делает ее малоприменимой для реальных задач&lt;br /&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;
* [[Тонкая куча]]&lt;br /&gt;
* [[Толстая куча на избыточном счетчике]]&lt;br /&gt;
* [[Куча Бродала-Окасаки]]&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Томас Кормен, Чарльз Лейзерсон, Рональд Ривест, Клиффорд Штайн — Алгоритмы: построение и анализ. — М.: Издательский дом «Вильямс», 2005. — С. 1296. — ISBN 5-8459-0857-4&lt;br /&gt;
* [[wikipedia:en:Числа Фибоначчи|Числа Фибоначчи {{---}} Википедия]]&lt;br /&gt;
* [[wikipedia:en:Fibonacci heap|Фибоначчиева куча {{---}} Википедия]]&lt;br /&gt;
* [https://www.cs.usfca.edu/~galles/visualization/FibonacciHeap.html Fibonacci heap visualization]&lt;br /&gt;
* [http://www.intuit.ru/department/algorithms/dscm/7/2.html Фибоначчиевы кучи — INTUIT.ru]&lt;br /&gt;
* [http://www.cs.duke.edu/courses/fall05/cps230/L-11.pdf Fibonacci Heaps {{---}} Duke University]&lt;br /&gt;
* [https://www.cs.princeton.edu/~wayne/teaching/fibonacci-heap.pdf Fibonacci Heaps {{---}} Princeton University]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Приоритетные очереди]]&lt;/div&gt;</summary>
		<author><name>92.242.59.6</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=2-3_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=60391</id>
		<title>2-3 дерево</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=2-3_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=60391"/>
				<updated>2017-01-31T08:58:11Z</updated>
		
		<summary type="html">&lt;p&gt;92.242.59.6: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Файл:23treemain.png|400px|Пример 2-3 дерева|thumb]]''' 2-3 дерево '''(англ. ''2-3 tree'') — структура данных, представляющая собой сбалансированное дерево поиска, такое что из каждого узла может выходить две или три ветви и глубина всех листьев одинакова. Является частным случаем [[B-дерево#B.2B-.D0.B4.D0.B5.D1.80.D0.B5.D0.B2.D0.BE|B+ дерева]].&lt;br /&gt;
== Свойства ==&lt;br /&gt;
2-3 дерево {{---}} сбалансированное дерево поиска, обладающее следующими свойствами:&lt;br /&gt;
*нелистовые вершины имеют либо &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt;, либо &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; сына,&lt;br /&gt;
*нелистовая вершина, имеющая двух сыновей, хранит максимум левого поддерева. Нелистовая вершина, имеющая трех сыновей, хранит два значения. Первое значение хранит максимум левого поддерева, второе максимум центрального поддерева,&lt;br /&gt;
*сыновья упорядочены по значению максимума поддерева сына,&lt;br /&gt;
*все листья лежат на одной глубине,&lt;br /&gt;
*высота 2-3 дерева &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;\mathtt{root}&amp;lt;/tex&amp;gt; {{---}} корень 2-3 дерева.&lt;br /&gt;
Каждый узел дерева обладает полями:&lt;br /&gt;
*&amp;lt;tex&amp;gt;\mathtt{parent}&amp;lt;/tex&amp;gt; {{---}} родитель узла,&lt;br /&gt;
*&amp;lt;tex&amp;gt;\mathtt{sons}&amp;lt;/tex&amp;gt; {{---}} сыновья узла, &lt;br /&gt;
*&amp;lt;tex&amp;gt;\mathtt{keys}&amp;lt;/tex&amp;gt; {{---}} ключи узла, &lt;br /&gt;
*&amp;lt;tex&amp;gt;\mathtt{length}&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;t&amp;lt;/tex&amp;gt; {{---}} текущая вершина в дереве. &lt;br /&gt;
&lt;br /&gt;
Изначально &amp;lt;tex&amp;gt;t = \mathtt{root}&amp;lt;/tex&amp;gt;. Будем просматривать ключи в узлах, пока узел не является листом. Рассмотрим два случая:&lt;br /&gt;
*у текущей вершины два сына. Если её значение меньше &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;t = \mathtt{t.sons[1]}&amp;lt;/tex&amp;gt;, иначе &amp;lt;tex&amp;gt;t = \mathtt{t.sons[0]}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*у текущей вершины три сына. Если второе значение меньше &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;t = \mathtt{t.sons[2]}&amp;lt;/tex&amp;gt;. Если первое значение меньше &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;t = \mathtt{t.sons[1]}&amp;lt;/tex&amp;gt;, иначе &amp;lt;tex&amp;gt;t = \mathtt{t.sons[0]}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 '''T''' search('''T''' x):&lt;br /&gt;
   Node t = root&lt;br /&gt;
   '''while''' (t не является листом)&lt;br /&gt;
     '''if''' (t.length == 2)&lt;br /&gt;
       '''if''' (t.keys[0] &amp;lt; x)&lt;br /&gt;
         t = t.sons[1]&lt;br /&gt;
       '''else''' &lt;br /&gt;
         t = t.sons[0]&lt;br /&gt;
     '''else''' '''if''' (t.keys[1] &amp;lt; x)&lt;br /&gt;
       t = t.sons[2]&lt;br /&gt;
     '''else''' '''if''' (t.keys[0] &amp;lt; x)&lt;br /&gt;
       t = t.sons[1]&lt;br /&gt;
     '''else''' &lt;br /&gt;
       t = t.sons[0]&lt;br /&gt;
   '''return''' t.keys[0]&lt;br /&gt;
&lt;br /&gt;
[[Файл:23treesearch.png|thumb|center|600px|Поиск элемента 19, оранжевые стрелки обозначают путь по дереву при поиске]]&lt;br /&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;t&amp;lt;/tex&amp;gt; {{---}} текущая вершина в дереве. Изначально &amp;lt;tex&amp;gt;t = \mathtt{root}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Если корня не существует {{---}} дерево пустое, то новый элемент и будет корнем (одновременно и листом). Иначе поступим следующим образом:&lt;br /&gt;
&lt;br /&gt;
Найдем сперва, где бы находился элемент, применив &amp;lt;tex&amp;gt;\mathtt{search(x)}&amp;lt;/tex&amp;gt;. Далее проверим есть ли у этого узла родитель, если его нет, то в дереве всего один элемент {{---}} лист. Возьмем этот лист и новый узел, и создадим для них родителя, лист и новый узел расположим в порядке возрастания.&lt;br /&gt;
&lt;br /&gt;
Если родитель существует, то подвесим к нему ещё одного сына. Если сыновей стало &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;, то разделим родителя на два узла, и повторим разделение теперь для его родителя, ведь у него тоже могло быть уже &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; сына, а мы разделили и у него стало на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; сына больше. (перед разделением обновим ключи).&lt;br /&gt;
&lt;br /&gt;
 '''function''' splitParent('''Node''' t):&lt;br /&gt;
  '''if''' (t.length &amp;gt; 3) &lt;br /&gt;
    Node a = Node(sons = {t.sons[2], t.sons[3]}, keys = {t.keys[2]}, parent = t.parent, length = 2)&lt;br /&gt;
    t.sons[2].parent = a&lt;br /&gt;
    t.sons[3].parent = a&lt;br /&gt;
    t.length = 2&lt;br /&gt;
    t.sons[2] = ''null''&lt;br /&gt;
    t.sons[3] = ''null''&lt;br /&gt;
    '''if''' (t.parent != ''null'')&lt;br /&gt;
      t.parent[t.length] = a&lt;br /&gt;
      t.length++&lt;br /&gt;
      сортируем сыновей у t.parent&lt;br /&gt;
      splitParent(t.parent)&lt;br /&gt;
    '''else'''                   &amp;lt;font color=green&amp;gt;// мы расщепили корень, надо подвесить его к общему родителю, который будет новым корнем&amp;lt;/font&amp;gt;&lt;br /&gt;
     Node t = root&lt;br /&gt;
     root.sons[0] = t&lt;br /&gt;
     root.sons[1] = a&lt;br /&gt;
     t.parent = root&lt;br /&gt;
     a.parent = root&lt;br /&gt;
     root.length = 2&lt;br /&gt;
     сортируем сыновей у root&lt;br /&gt;
Если сыновей стало &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt;, то ничего не делаем. Далее необходимо восстановить ключи на пути от новой вершины до корня:&lt;br /&gt;
 '''function''' updateKeys('''Node''' t): &lt;br /&gt;
   Node a = t.parent&lt;br /&gt;
   '''while''' (a != ''null'')&lt;br /&gt;
    '''for''' i = 0 .. a.length - 1&lt;br /&gt;
      a.keys[i] = max(a.sons[i]) &amp;lt;font color=green&amp;gt;// max {{---}} возвращает максимальное значение в поддереве.&amp;lt;/font&amp;gt;&lt;br /&gt;
    a = a.parent                 &amp;lt;font color=green&amp;gt;// Примечание: max легко находить, если хранить максимум &amp;lt;/font&amp;gt;&lt;br /&gt;
                                 &amp;lt;font color=green&amp;gt;// правого поддерева в каждом узле {{---}} это значение и будет max(a.sons[i])&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\mathtt{updateKeys}&amp;lt;/tex&amp;gt; необходимо запускать от нового узла.&lt;br /&gt;
Добавление элемента:&lt;br /&gt;
 '''function''' insert('''T''' x):&lt;br /&gt;
   Node n = Node(x)&lt;br /&gt;
   '''if''' (root == ''null'') &lt;br /&gt;
    root = n&lt;br /&gt;
    '''return'''&lt;br /&gt;
   Node a = searchNode(x)     &lt;br /&gt;
   '''if''' (a.parent == ''null'') &lt;br /&gt;
     Node t = root&lt;br /&gt;
     root.sons[0] = t&lt;br /&gt;
     root.sons[1] = n&lt;br /&gt;
     t.parent = root&lt;br /&gt;
     n.parent = root&lt;br /&gt;
     root.length = 2&lt;br /&gt;
     сортируем сыновей у root&lt;br /&gt;
   '''else''' &lt;br /&gt;
     Node p = a.parent&lt;br /&gt;
     p.sons[p.length] = n&lt;br /&gt;
     p.length++&lt;br /&gt;
     n.parent = p&lt;br /&gt;
     сортируем сыновей у p&lt;br /&gt;
     updateKeys(n) &lt;br /&gt;
     split(n)&lt;br /&gt;
   updateKeys(n) &lt;br /&gt;
Так как мы спускаемся один раз, и поднимаемся вверх при расщеплении родителей не более одного раза, то &amp;lt;tex&amp;gt;\mathtt{insert}&amp;lt;/tex&amp;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;
[[Файл:23treeinsert.png|thumb|center|Добавление элемента с ключом 6|600px]]&lt;br /&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;t&amp;lt;/tex&amp;gt; {{---}} текущий узел,&lt;br /&gt;
*&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt; {{---}} брат &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;,&lt;br /&gt;
*&amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; {{---}} отец &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;,&lt;br /&gt;
*&amp;lt;tex&amp;gt;np&amp;lt;/tex&amp;gt; {{---}} соседний брат &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt;,&lt;br /&gt;
*&amp;lt;tex&amp;gt;gp&amp;lt;/tex&amp;gt; {{---}} отец &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Пусть изначально &amp;lt;tex&amp;gt;t = \mathtt{searchNode(x)}&amp;lt;/tex&amp;gt; {{---}} узел, где находится &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если у &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; не существует родителя, то это корень (одновременно и единственный элемент в дереве). Удалим его.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; существует, и у него строго больше &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt; сыновей, то просто удалим &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;, а у &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; уменьшим количество детей.&lt;br /&gt;
&lt;br /&gt;
Если у родителя &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; два сына, рассмотрим возможные случаи (сперва везде удаляем &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;):&lt;br /&gt;
*&amp;lt;tex&amp;gt;np&amp;lt;/tex&amp;gt; не существует, тогда мы удаляем одного из сыновей корня, следовательно, другой сын становится новым корнем,&lt;br /&gt;
*у &amp;lt;tex&amp;gt;gp&amp;lt;/tex&amp;gt; оказалось &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt; сына, у &amp;lt;tex&amp;gt;np&amp;lt;/tex&amp;gt; оказалось &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt; сына. Подвесим &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;np&amp;lt;/tex&amp;gt; и удалим &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt;. Так как у &amp;lt;tex&amp;gt;gp&amp;lt;/tex&amp;gt; {{---}} родителя &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt;, оказалось тоже два сына,повторяем для &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; такие же рассуждения,&lt;br /&gt;
*у &amp;lt;tex&amp;gt;gp&amp;lt;/tex&amp;gt; оказалось &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; сына, у &amp;lt;tex&amp;gt;np&amp;lt;/tex&amp;gt; оказалось &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; сына. Просто заберем ближайшего к нам сына у &amp;lt;tex&amp;gt;np&amp;lt;/tex&amp;gt; и прицепим его к &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt;. Восстановим порядок в сыновьях &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt;. Теперь у &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; оказалось снова два сына и все узлы 2-3 дерева корректны,&lt;br /&gt;
*у &amp;lt;tex&amp;gt;gp&amp;lt;/tex&amp;gt; оказалось &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; сына, у &amp;lt;tex&amp;gt;np&amp;lt;/tex&amp;gt; оказалось &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt; сына. Подвесим &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;np&amp;lt;/tex&amp;gt; и удалим &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt;, а у &amp;lt;tex&amp;gt;gp&amp;lt;/tex&amp;gt; уменьшим количество детей. Так как у &amp;lt;tex&amp;gt;np&amp;lt;/tex&amp;gt; оказалось три сына, а у &amp;lt;tex&amp;gt;gp&amp;lt;/tex&amp;gt; все ещё больше одного сына, то все узлы 2-3 дерева корректны.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Обобщим алгоритм при удалении когда у родителя &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; два сына:&lt;br /&gt;
*Если &amp;lt;tex&amp;gt;np&amp;lt;/tex&amp;gt; не существует, то оказывается, что мы сейчас удаляем какого-то из сыновей корня (для определенности далее левого, с правым аналогично). Тогда теперь правый сын становится корнем. На этом удаление заканчивается.&lt;br /&gt;
&lt;br /&gt;
*Если &amp;lt;tex&amp;gt;np&amp;lt;/tex&amp;gt; существует, то удалим &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;, а его брата (&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;) перецепим к &amp;lt;tex&amp;gt;np&amp;lt;/tex&amp;gt;. Теперь у &amp;lt;tex&amp;gt;np&amp;lt;/tex&amp;gt; могло оказаться &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt; сына, поэтому повторим аналогичные действия из &amp;lt;tex&amp;gt;\mathtt{insert}&amp;lt;/tex&amp;gt;: вызовем &amp;lt;tex&amp;gt;\mathtt{updateKeys}(b)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{splitParent}(np)&amp;lt;/tex&amp;gt;. Теперь рекурсивно удалим &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В результате мы получаем корректное по структуре 2-3 дерево, но у нас есть нарушение в ключах в узлах, исправим их с помощью &amp;lt;tex&amp;gt;\mathtt{updateKeys()}&amp;lt;/tex&amp;gt;, запустившись от &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
[[Файл:23treedelete.png|thumb|center|Удаление элемента с ключом 2|1150px]]&lt;br /&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;t&amp;lt;/tex&amp;gt; {{---}} текущий узел.&lt;br /&gt;
В силу того, что наши узлы отсортированы по максимуму в поддереве, то следующий объект {{---}} это соседний лист справа. Попасть туда можно следующим образом:&lt;br /&gt;
будем подниматься вверх, пока у нас не появится первой возможности свернуть направо вниз. Как только мы свернули направо вниз, будем идти всегда влево. Таким образом, мы окажемся в соседнем листе. Если мы не смогли ни разу свернуть направо вниз, и пришли в корень, то следующего объекта не существует. Случай с предыдущим симметричен.&lt;br /&gt;
  '''T''' next('''T''' x):&lt;br /&gt;
    Node t = searchNode(x)&lt;br /&gt;
    '''if''' (t.keys[0] &amp;gt; x) &amp;lt;font color=green&amp;gt; //x не было в дереве, и мы нашли следующий сразу&amp;lt;/font&amp;gt;&lt;br /&gt;
      '''return''' t.keys[0]&lt;br /&gt;
    '''while''' (t != ''null'')&lt;br /&gt;
      t = t.parent&lt;br /&gt;
      '''if''' (можно свернуть направо вниз)&lt;br /&gt;
       в t помещаем вершину, в которую свернули&lt;br /&gt;
       '''while''' (пока t {{---}} не лист)&lt;br /&gt;
        t = t.sons[0]&lt;br /&gt;
      '''return''' t&lt;br /&gt;
    '''return''' t.keys[0]&lt;br /&gt;
     &lt;br /&gt;
&lt;br /&gt;
[[Файл:23treenext.png|thumb|center|400px|Путь при поиске следующего элемента после 2]]&lt;br /&gt;
&lt;br /&gt;
=== Нахождение m следующих элементов ===&lt;br /&gt;
B+ деревья, поддерживают операцию &amp;lt;tex&amp;gt;\mathtt{find}&amp;lt;/tex&amp;gt;, которая позволяет находить m следующих элементов. Наивная реализация выглядит следующим образом: будем вызывать &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt; раз поиск следующего элемента, такое решение работает за &amp;lt;tex&amp;gt;O(m  \log{n})&amp;lt;/tex&amp;gt;. Но 2-3 деревья, позволяют находить m следующих элементов за &amp;lt;tex&amp;gt;O(m + \log{n})&amp;lt;/tex&amp;gt;, что значительно ускоряет поиск при больших &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt;.&lt;br /&gt;
По построению, все листья у нас отсортированы в порядке возрастания, воспользуемся этим для нахождения m элементов. Нам необходимо связать листья, для этого модифицируем&lt;br /&gt;
&amp;lt;tex&amp;gt;\mathtt{insert}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{delete}&amp;lt;/tex&amp;gt;. Добавим к узлам следующие поля:&lt;br /&gt;
*&amp;lt;tex&amp;gt;\mathtt{right}&amp;lt;/tex&amp;gt; {{---}} указывает на правый лист,&lt;br /&gt;
*&amp;lt;tex&amp;gt;\mathtt{left}&amp;lt;/tex&amp;gt; {{---}} указывает на левый лист.&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; {{---}} добавленный узел.&lt;br /&gt;
Изменим &amp;lt;tex&amp;gt;\mathtt{insert}&amp;lt;/tex&amp;gt; следующим образом: в самом конце, после того как мы уже обновили все ключи, найдем &amp;lt;tex&amp;gt;\mathtt{next(t)}&amp;lt;/tex&amp;gt; и запишем ссылку на него в &amp;lt;tex&amp;gt;\mathtt{t.right}&amp;lt;/tex&amp;gt;. Аналогично с левым.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; {{---}} удаляемый узел. Изменим &amp;lt;tex&amp;gt;\mathtt{delete}&amp;lt;/tex&amp;gt; следующим образом: в самом начале, до удаления &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;, найдем следующий &amp;lt;tex&amp;gt;\mathtt{next}&amp;lt;/tex&amp;gt; и запишем в &amp;lt;tex&amp;gt;\mathtt{next.left}&amp;lt;/tex&amp;gt; правый лист относительно &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;. С левым поступим аналогично.&lt;br /&gt;
&lt;br /&gt;
В итоге, мы имеем двусвязный список в листьях, и чтобы нам вывести &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt; элементов, нам достаточно один раз найти нужный элемент и пробежаться вправо на &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:23treefindm.png|thumb|Изменение ссылок при добавлении нового элемента|thumb|center|800px]]&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[B-дерево]]&lt;br /&gt;
* [[Splay-дерево]]&lt;br /&gt;
* [[АВЛ-дерево]]&lt;br /&gt;
* [[Декартово дерево]]&lt;br /&gt;
* [[Красно-черное дерево]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [http://is.ifmo.ru/vis/tree23/tree23_ru.html is.ifmo.ru {{---}} Визуализатор 2-3 дерева]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/trees/2-3-2002 rain.ifmo.ru {{---}} Визуализатор 2-3 дерева]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/2-3-дерево Википедия {{---}} 2-3 дерево]&lt;br /&gt;
* Д. Кнут «Искусство программирования. Сортировка и поиск» {{---}} стр. 508-509&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Структуры данных]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>92.242.59.6</name></author>	</entry>

	</feed>