<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=188.130.155.157&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=188.130.155.157&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/188.130.155.157"/>
		<updated>2026-06-22T23:28:16Z</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=65313</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=65313"/>
				<updated>2018-05-10T18:57:11Z</updated>
		
		<summary type="html">&lt;p&gt;188.130.155.157: /* Удаление минимального элемента. Исправлена опечатка в удалении элемента из двусвязного списка. */&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:#008000&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>188.130.155.157</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%B0%D1%8F_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0&amp;diff=64814</id>
		<title>Быстрая сортировка</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%B0%D1%8F_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0&amp;diff=64814"/>
				<updated>2018-04-03T18:12:44Z</updated>
		
		<summary type="html">&lt;p&gt;188.130.155.157: /* Нерекурсивная реализация быстрой сортировки */ {{mxt|l}} перепутали с {{mxt|1}}&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Быстрая сортировка''' (англ. ''quick sort'', сортировка Хоара) {{---}} один из самых известных и широко используемых алгоритмов сортировки. Среднее время работы &amp;lt;Tex&amp;gt;O(n\log{n})&amp;lt;/Tex&amp;gt;, что является асимптотически оптимальным временем работы для алгоритма, основанного на сравнении. Хотя время работы алгоритма для массива из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов в худшем случае может составить &amp;lt;tex&amp;gt;\Theta(n^2)&amp;lt;/tex&amp;gt;, на практике этот алгоритм является одним из самых быстрых.&lt;br /&gt;
&lt;br /&gt;
==Алгоритм==&lt;br /&gt;
Быстрый метод сортировки функционирует по принципу &amp;quot;разделяй и властвуй&amp;quot;. &lt;br /&gt;
* Массив &amp;lt;tex&amp;gt; a[l \ldots r]&amp;lt;/tex&amp;gt; типа &amp;lt;tex&amp;gt; T &amp;lt;/tex&amp;gt; разбивается на два (возможно пустых) подмассива &amp;lt;tex&amp;gt; a[l \ldots q]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; a[q+1 \ldots r]&amp;lt;/tex&amp;gt;, таких, что каждый элемент &amp;lt;tex&amp;gt; a[l \ldots q]&amp;lt;/tex&amp;gt; меньше или равен &amp;lt;tex&amp;gt; a[q]&amp;lt;/tex&amp;gt;, который в свою очередь, не превышает любой элемент подмассива &amp;lt;tex&amp;gt; a[q+1 \ldots r]&amp;lt;/tex&amp;gt;. Индекс  вычисляется  в ходе процедуры разбиения.&lt;br /&gt;
* Подмассивы &amp;lt;tex&amp;gt; a[l \ldots q]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; a[q+1 \ldots r]&amp;lt;/tex&amp;gt; сортируются с помощью рекурсивного вызова процедуры быстрой сортировки.&lt;br /&gt;
* Поскольку подмассивы сортируются на месте, для их объединения не требуются никакие действия: весь массив &amp;lt;tex&amp;gt; a[l \ldots r]&amp;lt;/tex&amp;gt; оказывается отсортированным.&lt;br /&gt;
&lt;br /&gt;
==Псевдокод==&lt;br /&gt;
   '''void''' quicksort(a: '''T'''[n], '''int''' l, '''int''' r)&lt;br /&gt;
      '''if''' l &amp;lt; r&lt;br /&gt;
         '''int''' q = partition(a, l, r)&lt;br /&gt;
         quicksort(a, l, q)&lt;br /&gt;
         quicksort(a, q + 1, r)&lt;br /&gt;
Для сортировки всего массива необходимо выполнить процедуру &amp;lt;tex&amp;gt;\mathrm{quicksort(a, 0, length[a] - 1)}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Разбиение массива===&lt;br /&gt;
Основной шаг алгоритма сортировки {{---}} процедура &amp;lt;tex&amp;gt;\mathrm{partition}&amp;lt;/tex&amp;gt;, которая переставляет элементы массива &amp;lt;tex&amp;gt;a[l \ldots r]&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; a[(l + r) / 2] &amp;lt;/tex&amp;gt;. Далее начинается просмотр с левого конца массива, который продолжается до тех пор, пока не будет найден элемент, превосходящий по значению разделяющий элемент, затем выполняется просмотр, начиная с правого конца массива, который продолжается до тех пор, пока не отыскивается элемент, который по значению меньше разделяющего. Оба элемента, на которых просмотр был прерван, очевидно, находятся не на своих местах в разделенном массиве, и потому они меняются местами. Так продолжаем дальше, пока не убедимся в том, что слева от левого указателя не осталось ни одного элемента, который был бы больше по значению разделяющего, и ни одного элемента справа от правого указателя, которые были бы меньше по значению разделяющего элемента.&lt;br /&gt;
&lt;br /&gt;
Переменная &amp;lt;tex&amp;gt; v &amp;lt;/tex&amp;gt; сохраняет значение разделяющего элемента &amp;lt;tex&amp;gt; a[(l + r) / 2] &amp;lt;/tex&amp;gt;, a &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; представляет собой, соответственно, указатели левого и правого просмотра. Цикл разделения увеличивает значение &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; и уменьшает значение &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;, причем условие, что ни один элемент слева от &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; не больше &amp;lt;tex&amp;gt; v &amp;lt;/tex&amp;gt; и ни один элемент справа от &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; не меньше  &amp;lt;tex&amp;gt; v &amp;lt;/tex&amp;gt;, не нарушается. Как только значения указателей пересекаются, процедура разбиения завершается.&lt;br /&gt;
&lt;br /&gt;
   '''int''' partition(a: '''T'''[n], '''int''' l, '''int''' r)&lt;br /&gt;
      '''T''' v = a[(l + r) / 2]&lt;br /&gt;
      '''int''' i = l&lt;br /&gt;
      '''int''' j = r&lt;br /&gt;
      '''while''' (i &amp;lt;tex&amp;gt; \leqslant &amp;lt;/tex&amp;gt; j) &lt;br /&gt;
         '''while''' (a[i] &amp;lt; v)&lt;br /&gt;
            i++&lt;br /&gt;
         '''while''' (a[j] &amp;gt; v)&lt;br /&gt;
            j--&lt;br /&gt;
         '''if''' (i &amp;lt;tex&amp;gt; \geqslant &amp;lt;/tex&amp;gt; j) &lt;br /&gt;
            '''break'''&lt;br /&gt;
         swap(a[i++], a[j--])&lt;br /&gt;
      '''return''' j&lt;br /&gt;
&lt;br /&gt;
==Асимптотика==&lt;br /&gt;
===Худшее время работы===&lt;br /&gt;
Предположим, что мы разбиваем массив так, что одна часть содержит &amp;lt;tex&amp;gt;n - 1&amp;lt;/tex&amp;gt; элементов, а вторая {{---}} &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Поскольку процедура разбиения занимает время &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt;, для времени работы &amp;lt;tex&amp;gt;T(n)&amp;lt;/tex&amp;gt; получаем соотношение:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;T(n) = T(n - 1) + \Theta(n) = \sum\limits_{k=1}^{n} \Theta(k) = \Theta(\sum\limits_{k=1}^{n} k) = \Theta(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Мы видим, что при максимально несбалансированном разбиении время работы составляет &amp;lt;tex&amp;gt;\Theta(n^2)&amp;lt;/tex&amp;gt;. В частности, это происходит, если массив изначально отсортирован.&lt;br /&gt;
&lt;br /&gt;
===Способ построить массив с максимальным количеством сравнений при выборе среднего элемента в качестве опорного===&lt;br /&gt;
В некоторых алгоритмах быстрой сортировки в качестве опорного выбирается элемент, который стоит в середине рассматриваемого массива. Рассмотрим массив, на котором быстрая сортировка с выбором среднего элемента в качестве опорного сделает &amp;lt;tex&amp;gt;\Theta(n^2)&amp;lt;/tex&amp;gt; сравнений. Очевидно, что это будет достигаться при худшем случае (когда при каждом разбиении в одном массиве будет оказываться &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, а в другом &amp;lt;tex&amp;gt; n - 1 &amp;lt;/tex&amp;gt; элемент).&lt;br /&gt;
&lt;br /&gt;
Заполним сначала массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементами от &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;, затем применим следующий алгоритм (нумерация с нуля):&lt;br /&gt;
 &lt;br /&gt;
   '''void''' antiQsort(a: '''T'''[n])&lt;br /&gt;
      '''for''' i = 0 '''to''' n - 1 &lt;br /&gt;
         swap(a[i], a[i / 2])&lt;br /&gt;
Тогда на каждом шаге в качестве среднего элемента будет ставиться самый крупный элемент.&lt;br /&gt;
&lt;br /&gt;
При выполнении &amp;lt;tex&amp;gt;\mathrm{partition}&amp;lt;/tex&amp;gt; делается &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; сравнений из-за того, что с помощью индексов &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; мы проходим в лучшем случае &amp;lt;tex&amp;gt;\Omega(n)&amp;lt;/tex&amp;gt; элементов (если функция прекращает свою работу, как только индексы встречаются), в худшем случае &amp;lt;tex&amp;gt;O(2n)&amp;lt;/tex&amp;gt; элементов (если оба индекса полностью проходят массив). При каждом изменении индекса делается сравнение, значит, процедура &amp;lt;tex&amp;gt;\mathrm{partition}&amp;lt;/tex&amp;gt; делает &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; сравнений с точностью до константы.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим, какой элемент будет выбираться опорным на каждом шаге. &amp;lt;tex&amp;gt;\mathrm{antiQsort}&amp;lt;/tex&amp;gt; на каждом шаге меняет местами последний и центральный элементы, поэтому в центре оказывается самый крупный элемент. А &amp;lt;tex&amp;gt;\mathrm{partition}&amp;lt;/tex&amp;gt; делает абсолютно симметричные этой процедуре операции, но в другую сторону: меняет местами центральный элемент с последним, так что самый крупный элемент становится последним, а затем выполняет на массиве длины на один меньшей ту же операцию. Получается, что опорным всегда будет выбираться самый крупный элемент, так как &amp;lt;tex&amp;gt; \mathrm{antiQsort} &amp;lt;/tex&amp;gt; на массиве любой длины будет выполнять операции, обратные &amp;lt;tex&amp;gt;\mathrm{partition}&amp;lt;/tex&amp;gt;. Фактически, &amp;lt;tex&amp;gt;\mathrm{partition}&amp;lt;/tex&amp;gt; {{---}} это &amp;lt;tex&amp;gt;\mathrm{antiQsort}&amp;lt;/tex&amp;gt;, запущенная в другую сторону. Также стоит отметить, что процедура разбиения будет делать на каждом шаге только одну смену элементов местами. Сначала &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; дойдет до середины массива, до опорного элемента, &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; останется равным индексу последнего элемента. Затем произойдет &amp;lt;tex&amp;gt;\mathrm{swap}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; снова начнет увеличиваться, пока не дойдет до последнего элемента, &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; опять не изменит свою позицию. Потом произойдет выход из &amp;lt;tex&amp;gt;\mathrm{while}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Разбиение массива будет произведено &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; раз, потому что разбиение производится на массивы длины &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; n - 1 &amp;lt;/tex&amp;gt; из-за того, что на каждом шаге разбиения в качестве опорного будет выбираться самый крупный элемент (оценка на худшее время работы доказана выше).  Следовательно, на массиве, который строится описанным выше способом, выполняется &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\mathrm{partition}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; сравнений для каждого выполнения &amp;lt;tex&amp;gt;\mathrm{partition}&amp;lt;/tex&amp;gt;. Тогда быстрая сортировка выполнит &amp;lt;tex&amp;gt;\Theta(n^2)&amp;lt;/tex&amp;gt; сравнений для массива, построенного таким способом.&lt;br /&gt;
&lt;br /&gt;
===Способ построить массив с максимальным количеством сравнений при детерминированном выборе опорного элемента===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим алгоритм построения массива, на котором быстрая сортировка с детерминированным выбором опорного элемента будет делать максимальное (в данном случае {{---}} &amp;lt;tex&amp;gt;\Theta(n^2)&amp;lt;/tex&amp;gt;) количество сравнений. Такое число сравнений достигается при разбиении на массивы длиной &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;n-1&amp;lt;/tex&amp;gt; на каждой итерации.  &lt;br /&gt;
Создадим массив &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, заполненный элементами типа &amp;lt;tex&amp;gt;pair&amp;lt;/tex&amp;gt;. Такой элемент хранит пару значений &amp;lt;tex&amp;gt;(val, key)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;val&amp;lt;/tex&amp;gt; {{---}} элемент массива, а &amp;lt;tex&amp;gt;key&amp;lt;/tex&amp;gt; {{---}} индекс. Изначально  &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; элемент имеет вид &amp;lt;tex&amp;gt;(0, i)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее, запустим для данного массива алгоритм быстрой сортировки. Сравниваем два элемента типа &amp;lt;tex&amp;gt;pair&amp;lt;/tex&amp;gt; по их значениям &amp;lt;tex&amp;gt;val&amp;lt;/tex&amp;gt;. На каждом шаге будем выполнять следующие действия: при обращении к &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ому элементу в качестве опорного на шаге под номером &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, присвоим &amp;lt;tex&amp;gt;val = n-k+1&amp;lt;/tex&amp;gt; для элемента &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;. Затем выполним шаг сортировки. После завершения работы алгоритма быстрой сортировки, дополнительно отсортируем получившиеся элементы &amp;lt;tex&amp;gt;pair&amp;lt;/tex&amp;gt; по значениям &amp;lt;tex&amp;gt;key&amp;lt;/tex&amp;gt;. Искомым будет являться массив элементов &amp;lt;tex&amp;gt;val&amp;lt;/tex&amp;gt; в соответствующей последовательности. &lt;br /&gt;
 &lt;br /&gt;
Пример для &amp;lt;tex&amp;gt;n = 4&amp;lt;/tex&amp;gt;, при последовательном выборе опорных элементов &amp;lt;tex&amp;gt;2, 2, 1, 1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!colspan=&amp;quot;8&amp;quot;| Построение массива&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
! Шаг 1.0 || Шаг 1.1 || Шаг 1.2 || Шаг 2.0 || Шаг 2.1 || Шаг 2.2 || Шаг 3.0&lt;br /&gt;
|-align=&amp;quot;right&amp;quot;&lt;br /&gt;
|style=&amp;quot;text-align: center;&amp;quot;|1 2 3 4 &amp;lt;br&amp;gt; 0 '''0''' 0 0&lt;br /&gt;
| style=&amp;quot;text-align: center;&amp;quot;|1 2 3 4 &amp;lt;br&amp;gt; 0 '''4''' 0 0&lt;br /&gt;
|style=&amp;quot;text-align: center;&amp;quot;|1 4 3 2 &amp;lt;br&amp;gt; 0 0 0 '''4'''&lt;br /&gt;
|style=&amp;quot;text-align: center;&amp;quot;|1 4 3 2 &amp;lt;br&amp;gt; 0 '''0''' 0 4&lt;br /&gt;
|style=&amp;quot;text-align: center;&amp;quot;|1 4 3 2 &amp;lt;br&amp;gt; 0 '''3''' 0 4&lt;br /&gt;
|style=&amp;quot;text-align: center;&amp;quot;|1 3 4 2 &amp;lt;br&amp;gt; 0 0 '''3''' 4&lt;br /&gt;
|style=&amp;quot;text-align: center;&amp;quot;|1 3 4 2 &amp;lt;br&amp;gt; '''0''' 0 3 4&lt;br /&gt;
|-&lt;br /&gt;
! Шаг 3.1 || Шаг 3.2 || Шаг 4.0 || Шаг 4.1 || Шаг 4.2 || colspan=&amp;quot;2&amp;quot; style=&amp;quot;vertical-align: middle;&amp;quot;| Результат&lt;br /&gt;
&lt;br /&gt;
|-align=&amp;quot;right&amp;quot;&lt;br /&gt;
|style=&amp;quot;text-align: center;&amp;quot;|1 3 4 2 &amp;lt;br&amp;gt; '''2''' 0 3 4&lt;br /&gt;
|style=&amp;quot;text-align: center;&amp;quot;|3 1 4 2 &amp;lt;br&amp;gt; 0 '''2''' 3 4&lt;br /&gt;
|style=&amp;quot;text-align: center;&amp;quot;|3 1 4 2 &amp;lt;br&amp;gt; '''0''' 2 3 4&lt;br /&gt;
|style=&amp;quot;text-align: center;&amp;quot;|3 1 4 2 &amp;lt;br&amp;gt; '''1''' 2 3 4&lt;br /&gt;
|style=&amp;quot;text-align: center;&amp;quot;|3 1 4 2 &amp;lt;br&amp;gt; '''1''' 2 3 4&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;vertical-align: middle;&amp;quot; |'''1 2 3 4''' &amp;lt;br&amp;gt; '''2 4 1 3'''&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
!colspan=&amp;quot;8&amp;quot; | Итоговый массив&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; colspan=&amp;quot;8&amp;quot;|&lt;br /&gt;
&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;2 4 1 3&amp;lt;/font&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Покажем, почему на данном массиве будет достигаться максимальное время работы быстрой сортировки. На этапе построения мы каждый раз присваивали опорному элементу минимальное значение. Следовательно, при выполнении &amp;lt;tex&amp;gt;\mathrm{quicksort}&amp;lt;/tex&amp;gt; алгоритм в качестве опорного всегда будет выбирать наибольший элемент массива (выборка будет производится в том же порядке ввиду детерминированности определения опорного элемента). &lt;br /&gt;
Таким образом, так как каждый раз массив разбивается на две части {{---}} большие или равные опорному элементы и меньшие его {{---}} на каждом шаге имеем разбиение на массивы длины &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;n-1&amp;lt;/tex&amp;gt;, чего мы, собственно, и добивались. При таком выполнении алгоритма происходит &amp;lt;tex&amp;gt;\Theta(n^2)&amp;lt;/tex&amp;gt; разделений на два подмассива, и на каждом разделении выполняется &amp;lt;tex&amp;gt;\Theta(n^2)&amp;lt;/tex&amp;gt; сравнений. &lt;br /&gt;
Следовательно, на данном массиве быстрая сортировка работает за &amp;lt;tex&amp;gt;\Theta(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Среднее время работы===&lt;br /&gt;
{{&lt;br /&gt;
Лемма&lt;br /&gt;
|statement=Время работы алгоритма быстрой сортировки равно &amp;lt;tex&amp;gt;O(n \log n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=Пусть &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; {{---}} полное количество сравнений элементов с опорным за время работы сортировки. Нам необходимо вычислить полное количество сравнений.&lt;br /&gt;
Переименуем элементы массива как &amp;lt;tex&amp;gt;z_1 \ldots z_n&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;z_i&amp;lt;/tex&amp;gt; наименьший по порядку элемент.&lt;br /&gt;
Также введем множество &amp;lt;tex&amp;gt;Z_{ij} = \{z_i, z_{i+1} \ldots z_j\}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сравнение каждой пары элементов происходит не больше одного раза, так как элемент сравнивается с опорным, а опорный элемент после разбиения больше не будет участвовать в сравнении.&lt;br /&gt;
&lt;br /&gt;
Поскольку каждая пара элементов сравнивается не более одного раза, полное количество сравнений выражается как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;X = \sum\limits_{i=1}^{n-1}\sum\limits_{j=i+1}^{n} X_{ij}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;X_{ij} = 1&amp;lt;/tex&amp;gt; если произошло сравнение &amp;lt;tex&amp;gt;z_i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;z_j&amp;lt;/tex&amp;gt;  и &amp;lt;tex&amp;gt;X_{ij} = 0&amp;lt;/tex&amp;gt;, если сравнения не произошло.&lt;br /&gt;
&lt;br /&gt;
Применим к обоим частям равенства операцию вычисления матожидания и воспользовавшись ее линейностью получим&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E[X] = E\left[\sum\limits_{i=1}^{n-1}\sum\limits_{j=i+1}^{n} X_{ij}\right] = \sum\limits_{i=1}^{n-1}\sum\limits_{j=i+1}^{n} E[X_{ij}] = \sum\limits_{i=1}^{n-1}\sum\limits_{j=i+1}^{n} Pr\{z_i&amp;lt;/tex&amp;gt; сравнивается с &amp;lt;tex&amp;gt;z_j\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Осталось вычислить величину &amp;lt;tex&amp;gt;Pr\{z_i&amp;lt;/tex&amp;gt; сравнивается с &amp;lt;tex&amp;gt;z_j\}&amp;lt;/tex&amp;gt; {{---}} вероятность того, что &amp;lt;tex&amp;gt;z_i&amp;lt;/tex&amp;gt; сравнивается с &amp;lt;tex&amp;gt;z_j&amp;lt;/tex&amp;gt;. Поскольку предполагается, что все элементы в массиве различны, то при выборе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; в качестве опорного элемента впоследствии не будут сравниваться никакие &amp;lt;tex&amp;gt;z_i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;z_j&amp;lt;/tex&amp;gt; для которых &amp;lt;tex&amp;gt;z_i &amp;lt; x &amp;lt; z_j&amp;lt;/tex&amp;gt;. С другой стороны, если &amp;lt;tex&amp;gt;z_i&amp;lt;/tex&amp;gt; выбран в качестве опорного, то он будет сравниваться с каждым элементом &amp;lt;tex&amp;gt;Z_{ij}&amp;lt;/tex&amp;gt; кроме себя самого. Таким образом элементы &amp;lt;tex&amp;gt;z_i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;z_j&amp;lt;/tex&amp;gt; сравниваются тогда и только тогда когда первым в множестве &amp;lt;tex&amp;gt;Z_{ij}&amp;lt;/tex&amp;gt; опорным элементом был выбран один из них.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;Pr\{z_i&amp;lt;/tex&amp;gt; сравнивается с &amp;lt;tex&amp;gt;z_j\} = &lt;br /&gt;
Pr\{&amp;lt;/tex&amp;gt;первым опорным элементом был &amp;lt;tex&amp;gt;z_i&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;z_j\} = &lt;br /&gt;
Pr\{&amp;lt;/tex&amp;gt;первым опорным элементом был &amp;lt;tex&amp;gt;z_i\} +&lt;br /&gt;
Pr\{&amp;lt;/tex&amp;gt;первым опорным элементом был &amp;lt;tex&amp;gt;z_j\} = &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; =\dfrac {1}{j-i+1} + \dfrac {1}{j-i+1}  = \dfrac {2}{j-i+1} &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; E[X] = \sum\limits_{i=1}^{n-1}\sum\limits_{j=i+1}^{n} \dfrac {2}{j-i+1} = \sum\limits_{i=1}^{n-1}\sum\limits_{k=1}^{n-i} \dfrac 2{k+1} &amp;lt; \sum\limits_{i=1}^{n-1}\sum\limits_{k=1}^{n-i} \dfrac 2{k} &amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;= \sum\limits_{i=1}^{n-1}O(\log n) = O(n \log n) &amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Mатожидание времени работы быстрой сортировки будет &amp;lt;tex&amp;gt;O(n \log n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Модификации==&lt;br /&gt;
&lt;br /&gt;
===Нерекурсивная реализация быстрой сортировки===&lt;br /&gt;
Для выполнения быстрой сортировки можно воспользоваться [[Стек | стеком]], в котором в виде сортируемых подмассивов содержится перечень действий, которые предстоит выполнить. Каждый раз когда возникает необходимость в обработке подмассива, он выталкивается из стека. После разделения массива получаются два подмассива, требующих дальнейшей обработки, которые и заталкиваются в стек.&lt;br /&gt;
Представленная ниже нерекурсивная реализация использует стек, заменяя рекурсивные вызовы помещением в стек параметров функции, а вызовы процедур и выходы из них — циклом, который осуществляет выборку параметров из стека и их обработку, пока стек не пуст. Мы помещаем больший из двух подмассивов в стек первым с тем, чтобы максимальная глубина стека при сортировке &amp;lt;tex&amp;gt;N&amp;lt;/tex&amp;gt; элементов не превосходила величины &amp;lt;tex&amp;gt;\log n&amp;lt;/tex&amp;gt;. &lt;br /&gt;
   '''void''' quicksort(a: '''T'''[n], '''int''' l, '''int''' r)&lt;br /&gt;
      '''stack'''&amp;lt; '''pair'''&amp;lt;'''int''','''int'''&amp;gt; &amp;gt; s   &lt;br /&gt;
      s.push(l, r)&lt;br /&gt;
      '''while''' (s.isNotEmpty)&lt;br /&gt;
         (l, r) = s.pop()&lt;br /&gt;
         '''if''' (r &amp;lt;tex&amp;gt; \leqslant &amp;lt;/tex&amp;gt; l)&lt;br /&gt;
            '''continue'''&lt;br /&gt;
         '''int''' i = partition(a, l, r)&lt;br /&gt;
         '''if''' (i - l &amp;gt; r - i) &lt;br /&gt;
            s.push(l, i - 1)&lt;br /&gt;
            s.push(i + 1, r)&lt;br /&gt;
         '''else'''&lt;br /&gt;
            s.push(i + 1, r)&lt;br /&gt;
            s.push(l, i - 1)&lt;br /&gt;
&lt;br /&gt;
В качестве альтернативного варианта можно использовать обычную рекурсивную версию, в которой вместо того, чтобы после разделения массива вызывать рекурсивно процедуру разделения для обоих найденных подмассивов, рекурсивный вызов делается только для меньшего подмассива, а больший обрабатывается в цикле в пределах этого же вызова процедуры. С точки зрения эффективности в среднем случае разницы практически нет: накладные расходы на дополнительный рекурсивный вызов и на организацию сравнения длин подмассивов и цикла — примерно одного порядка. Зато глубина рекурсии ни при каких обстоятельствах не превысит &amp;lt;tex&amp;gt;\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;
Выбор медианы из первого, среднего и последнего элементов в качестве разделяющего элемента и отсечение рекурсии меньших подмассивов может &lt;br /&gt;
привести к существенному повышению эффективности быстрой сортировки. Функция &amp;lt;tex&amp;gt;\mathrm{median}&amp;lt;/tex&amp;gt; возвращает индекс элемента, являющегося медианой трех элементов. После этого он и средний элемент массива меняются местами, при этом медиана становится разделяющим элементом. Массивы небольшого размера (длиной &amp;lt;tex&amp;gt; M = 11&amp;lt;/tex&amp;gt; и меньше) в процессе разделения игнорируются, затем для окончания сортировки используется [[Сортировка вставками | сортировка вставками]]. &lt;br /&gt;
&lt;br /&gt;
   '''const int''' M = 10&lt;br /&gt;
   '''void''' quicksort(a: '''T'''[n], '''int''' l, '''int''' r)&lt;br /&gt;
      '''if''' (r - l &amp;lt;tex&amp;gt; \leqslant &amp;lt;/tex&amp;gt; M)&lt;br /&gt;
         insertion(a, l, r)&lt;br /&gt;
         '''return'''&lt;br /&gt;
      '''int''' med = median(a[l], a[(l + r) / 2], a[r])&lt;br /&gt;
      swap(a[med], a[(l + r) / 2])&lt;br /&gt;
      '''int''' i = partition(a, l, r)&lt;br /&gt;
      quicksort(a, l, i)&lt;br /&gt;
      quicksort(a, i + 1, r)&lt;br /&gt;
&lt;br /&gt;
Вообще, можно применять любые эвристики по выбору опорного элемента. Например, в стандартной реализации в Java в качестве разделяющего выбирается средний из 7 элементов, равномерно распределённых по массиву.&lt;br /&gt;
&lt;br /&gt;
===Быстрая сортировка с разделением на три части===&lt;br /&gt;
&lt;br /&gt;
Когда в сортируемом массиве имеется множество повторяющихся ключей предыдущие реализации быстрой сортировки можно существенно улучшить. Например массив, который состоит из равных ключей, вовсе не нуждается в дальнейшей сортировке, однако предыдущие реализации продолжают процесс разделения, подвергая обработке все более мелкие подмассивы, независимо от того, насколько большим является исходный файл. &lt;br /&gt;
&lt;br /&gt;
В основу программы положено разделение массива на три части: &lt;br /&gt;
на элементы,меньшие разделяющего элемента &amp;lt;tex&amp;gt; a[l] \ldots a[i]&amp;lt;/tex&amp;gt;, &lt;br /&gt;
элементы, равные разделяющему элементу &amp;lt;tex&amp;gt;a[i+1] \ldots a[j-1]&amp;lt;/tex&amp;gt;,&lt;br /&gt;
и элементы большие разделяющего элемента &amp;lt;tex&amp;gt;a[j] \ldots a[r]&amp;lt;/tex&amp;gt;. &lt;br /&gt;
После этого сортировка завершается двумя рекурсивными вызовами.&lt;br /&gt;
&lt;br /&gt;
[[Файл:G3.png |400px|thumb|center| Разделение массива &amp;lt;tex&amp;gt; a &amp;lt;/tex&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Элементы массива равные разделяющему элементу находятся между &amp;lt;tex&amp;gt; l &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; p &amp;lt;/tex&amp;gt; и между &amp;lt;tex&amp;gt; q &amp;lt;/tex&amp;gt; и  &amp;lt;tex&amp;gt; r &amp;lt;/tex&amp;gt;. В разделяющем цикле, когда указатели просмотра перестают изменяться и выполняется обмен значениями &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt;, каждый из этих элементов проверяется на предмет равенства разделяющему элементу. Если элемент, который сейчас находится слева, равен разделяющему элементу, то при помощи операции обмена он помещается в левую часть массива, если элемент, который сейчас находится справа, равен разделяющему элементу, то в результате операции обмена он помещается в правую часть массива. &lt;br /&gt;
После того как указатели пересекутся, элементы, равные разделяющему элементу и находящиеся на разных концах массива, после операции обмена попадают в свои &lt;br /&gt;
окончательные позиции. После этого указанные ключи могут быть исключены из подмассивов, для которых выполняются последующие рекурсивные вызовы. &lt;br /&gt;
&lt;br /&gt;
   '''void''' quicksort(a: '''T'''[n], '''int''' l, '''int''' r)&lt;br /&gt;
      '''T''' v = a[r]&lt;br /&gt;
      '''if''' (r &amp;lt;tex&amp;gt; \leqslant &amp;lt;/tex&amp;gt; l)&lt;br /&gt;
         '''return'''&lt;br /&gt;
      '''int''' i = l&lt;br /&gt;
      '''int''' j = r - 1&lt;br /&gt;
      '''int''' p = l - 1&lt;br /&gt;
      '''int''' q = r&lt;br /&gt;
      '''while''' ''(i &amp;lt;tex&amp;gt; \leqslant &amp;lt;/tex&amp;gt; j)'' &lt;br /&gt;
         '''while''' (a[i] &amp;lt; v) &lt;br /&gt;
            i++&lt;br /&gt;
         '''while''' (a[j] &amp;gt; v) &lt;br /&gt;
            j--&lt;br /&gt;
         '''if''' (i &amp;lt;tex&amp;gt; \geqslant &amp;lt;/tex&amp;gt; j)&lt;br /&gt;
            '''break'''&lt;br /&gt;
         swap(a[i], a[j])&lt;br /&gt;
         '''if''' (a[i] == v)&lt;br /&gt;
            p++&lt;br /&gt;
            swap(a[p], a[i])&lt;br /&gt;
         i++&lt;br /&gt;
         '''if''' (a[j] == v)&lt;br /&gt;
            q--&lt;br /&gt;
            swap(a[q], a[j])&lt;br /&gt;
         j--&lt;br /&gt;
      swap(a[i], a[r])&lt;br /&gt;
      j = i - 1&lt;br /&gt;
      i++&lt;br /&gt;
      '''for''' ('''int''' k = l; k &amp;lt;tex&amp;gt; \leqslant &amp;lt;/tex&amp;gt; p; k++, j--) &lt;br /&gt;
         swap(a[k], a[j])&lt;br /&gt;
      '''for''' ('''int''' k = r - 1; k &amp;lt;tex&amp;gt; \geqslant &amp;lt;/tex&amp;gt; q; k--, i++) &lt;br /&gt;
         swap(a[k], a[i]) &lt;br /&gt;
      quicksort(a, l, j) &lt;br /&gt;
      quicksort(a, i, r)&lt;br /&gt;
&lt;br /&gt;
===Параллельная сортировка===&lt;br /&gt;
&lt;br /&gt;
Еще одной оптимизацией является [[PSRS-сортировка|параллельная сортировка]] на основе быстрой. &lt;br /&gt;
Пусть, исходный набор данных расположен на первом процессоре, с него начинается работа алгоритма. Затем исходный массив окажется разделенным на две части, меньшая из которых передастся другому свободному процессору, большая останется на исходном для дальнейшей обработки. Далее обе части опять будут разделены и опять на двух исходных останутся большие части, а меньшие отправятся другим процессорам. В этом заключается ускорение алгоритма. При задействовании всех процессоров, все части параллельно будут сортироваться последовательным алгоритмом.&lt;br /&gt;
&lt;br /&gt;
===Introsort===&lt;br /&gt;
&lt;br /&gt;
Для предотвращения ухудшения времени работы быстрой сортировки до &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt; при неудачных входных данных, также можно использовать алгоритм сортировки Introsort.&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;
==См. также==&lt;br /&gt;
* [[Сортировка Шелла]]&lt;br /&gt;
* [[Сортировка кучей]]&lt;br /&gt;
* [[Сортировка слиянием]]&lt;br /&gt;
* [[Timsort]]&lt;br /&gt;
* [[Smoothsort]]&lt;br /&gt;
* [[PSRS-сортировка]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [[wikipedia:ru:Быстрая сортировка|Википедия {{---}} Быстрая сортировка]]&lt;br /&gt;
* [[wikipedia:en:Quicksort|Wikipedia {{---}} Quicksort]]&lt;br /&gt;
* [[wikipedia:en:Introsort|Wikipedia {{---}} Introsort]]&lt;br /&gt;
* ''Т. Кормен, Ч. Лейзерсон, Р. Ривест'': Алгоритмы: построение и анализ глава 7&lt;br /&gt;
* ''Р. Седжвик'': Фундаментальные алгоритмы на С++ части 1 - 4&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Сортировка]]&lt;br /&gt;
[[Категория: Сортировки на сравнениях]]&lt;/div&gt;</summary>
		<author><name>188.130.155.157</name></author>	</entry>

	</feed>