<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=217.71.235.207&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=217.71.235.207&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/217.71.235.207"/>
		<updated>2026-05-19T16:51:00Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47625</id>
		<title>АВЛ-дерево</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47625"/>
				<updated>2015-06-05T16:45:40Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''АВЛ-дерево''' (англ. ''AVL-Tree'') {{---}} сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.&lt;br /&gt;
&lt;br /&gt;
АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г. М. Адельсона-Вельского и Е. М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962 году.&lt;br /&gt;
&lt;br /&gt;
== Высота дерева ==&lt;br /&gt;
{{Теорема &lt;br /&gt;
|statement=АВЛ-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключами имеет высоту &amp;lt;tex&amp;gt;h = O(\log N)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
||proof=&lt;br /&gt;
&lt;br /&gt;
Высоту поддерева с корнем &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; будем обозначать как &amp;lt;tex&amp;gt;h(x)&amp;lt;/tex&amp;gt;, высоту поддерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; {{---}} как &amp;lt;tex&amp;gt;h(T)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Пусть &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt;, где            &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фибоначчи.  &lt;br /&gt;
|proof=&lt;br /&gt;
Если &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;. Тогда, как легко видеть, &amp;lt;tex&amp;gt;m_{h+2} = m_{h+1} + m_h + 1&amp;lt;/tex&amp;gt;. Равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; докажем по индукции. &lt;br /&gt;
&lt;br /&gt;
База индукции &amp;lt;tex&amp;gt;m_1 = F_3 - 1&amp;lt;/tex&amp;gt; {{---}} верно, &amp;lt;tex&amp;gt;m_1 = 1, F_3 = 2&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Допустим &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} верно. &lt;br /&gt;
&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;m_{h+1} = m_h + m_{h-1} + 1 = F_{h+2} - 1 + F_{h+1} - 1 + 1 = F_{h+3} - 1&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Таким образом, равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} доказано.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\varphi = \dfrac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. То есть&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n \geqslant \varphi^{h}&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 h&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом,  получаем, что высота AVL-дерева из n вершин {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Балансировка ==&lt;br /&gt;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &amp;lt;tex&amp;gt;|h(L) - h(R)| = 2&amp;lt;/tex&amp;gt;, изменяет связи предок-потомок в поддереве данной вершины так, чтобы восстановилось свойство дерева &amp;lt;tex&amp;gt;|h(L) - h(R)| \leqslant 1&amp;lt;/tex&amp;gt;, иначе ничего не меняет.&lt;br /&gt;
Для балансировки будем хранить для каждой вершины разницу между высотой её левого и правого поддерева &amp;lt;tex&amp;gt;diff[i] = h(L) - h(R)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для балансировки вершины используются один из 4 типов вращений:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
 !Тип вращения&lt;br /&gt;
 !Иллюстрация&lt;br /&gt;
 !Когда используется&lt;br /&gt;
 !Расстановка балансов&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое левое вращение''' (''Small left rotation'')&lt;br /&gt;
 |  [[Файл:avl_u1.jpg|2000x200px]]  &lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение''' (англ. Big left rotation'')&lt;br /&gt;
 | [[Файл:avl_u2.jpg|2000x200px]]&lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; , &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |}&lt;br /&gt;
Малый левый поворот:&lt;br /&gt;
    rotateleft(node a) {&lt;br /&gt;
        node b = a.right&lt;br /&gt;
        a.right = b.left&lt;br /&gt;
        b.left = a&lt;br /&gt;
        fixheight(a)&lt;br /&gt;
        fixheight(b)&lt;br /&gt;
    }&lt;br /&gt;
Большой правый поворот пишется проще:&lt;br /&gt;
    bigrotateleft(node a) {&lt;br /&gt;
        rotateright(a.right)&lt;br /&gt;
        rotateleft(a)&lt;br /&gt;
    }&lt;br /&gt;
Малое правое и большое правое вращение определяются симметрично малому левому и большому левому вращению.&lt;br /&gt;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; и не может увеличиться.&lt;br /&gt;
&lt;br /&gt;
Все операции вращения, очевидно, требуют &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
&lt;br /&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;. Если мы стоим в вершине &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и нам надо идти в поддерево, которого нет, то делаем ключ &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; листом, а вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; его корнем. Дальше поднимаемся вверх по пути поиска и пересчитываем баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
Так как в процессе добавления вершины мы рассматриваем не более, чем &amp;lt;tex&amp;gt; O(h) &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;
Если вершина {{---}} лист, то [[Дерево поиска, наивная реализация#Удаление|удалим]] её, иначе найдём самую близкую по значению вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, переместим её на место удаляемой вершины и [[Дерево поиска, наивная реализация#Удаление|удалим]] вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. От удалённой вершины будем подниматься вверх к корню и пересчитывать баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; уменьшается на единицу, если из правого, то увеличивается на единицу. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
В результате указанных действий на удаление вершины и балансировку суммарно тратится, как и ранее, &amp;lt;tex&amp;gt; O(h) &amp;lt;/tex&amp;gt; операций. Таким образом, требуемое количество действий {{---}} &amp;lt;tex&amp;gt; O(\log{n}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Поиск вершины, минимум/максимум в дереве, etc. ===&lt;br /&gt;
Остальные операции не меняют структуры дерева, поэтому выполняются так же, как и в [[Дерево поиска, наивная реализация|наивной реализации]] дерева поиска.&lt;br /&gt;
===Слияние двух AVL-деревьев===&lt;br /&gt;
&lt;br /&gt;
Дано два дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, все ключи в &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;h(T_1) \leqslant h(T_2)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В дереве &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; удаляем самую правую вершину, назовём её &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;. Высота дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; может уменьшиться на единицу. В дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; идём от корня всегда в левое поддерево и, когда высота этого поддерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; будет равна высоте дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, делаем новое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; будет вершина &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, левым поддеревом будет дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, а правым дерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;. Теперь в дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; у вершины, в которой мы остановились при спуске, левым поддеревом делаем дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; до слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u3.jpg|340px]]&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; после слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u4.jpg|300px]]&lt;br /&gt;
&lt;br /&gt;
===Алгоритм разделения AVL-дерева на два===&lt;br /&gt;
====Алгоритм первый====&lt;br /&gt;
Пусть у нас есть дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Мы должны разбить его на два дерева &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; такие, что &amp;lt;tex&amp;gt;T_{1} \leqslant x&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;x &amp;lt; T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Предположим, что корень нашего дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, в таком случае все левое поддерево вместе с корнем после разделения отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда рекурсивно спускаемся в правое поддерево и там проверяем это условие (так как часть правого поддерева тоже может содержать ключи &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;). Если же корень оказался &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, то мы спускаемся той же рекурсией, но только в левое поддерево и ищем там.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. В таком случае этот корень со своим левым поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Поэтому мы делаем следующее: запоминаем ссылку на правое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, удаляем корень, запоминая его значение (не меняя конфигурацию дерева, то есть просто делаем ссылки на него NULL'ами). Таким образом, мы отделяем сбалансированное АВЛ-дерево (бывшее левое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Делаем новую вершину со значением бывшего корня правым листом самой правой вершины &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Обозначим полученное дерево за &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;. Теперь нам нужно объединить его с уже построенным ранее &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (оно может быть пустым, если мы первый раз нашли такое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Для этого мы ищем в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; самое правое поддерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; высоты, равной высоте &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (спускаясь от корня всегда в правые поддеревья). Делаем новое дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, сливая &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (очевидно, все ключи в &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, поэтому мы можем это сделать). Теперь в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; у отца вершины, в которой мы остановились при поиске дерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;, правым поддеревом делаем дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt; и запускаем балансировку. После нужно спуститься в правое поддерево бывшего дерева &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (по ссылке, которую мы ранее запомнили) и обработать его.&lt;br /&gt;
&lt;br /&gt;
Если мы пришли в поддерево &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, совершаем аналогичные действия: делаем NULL'ами ссылки на корень &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, запоминая ссылку на его левое поддерево. Делаем новую вершину со значением бывшего корня левым листом самой левой вершины &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt; и запускаем балансировку. Объединяем полученное АВЛ-дерево с уже построенным ранее &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; аналогичным первому случаю способом, только теперь мы ищем самое левое поддерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рассмотри пример (рис. 1). Цветом выделены поддеревья, которые после разделения должны отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;x = 76&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL.jpg|thumb|left|525px|Рис. 1. Разделение АВЛ-дерева на два.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Корень дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, поэтому он со всем выделенным поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. По описанному выше алгоритму отделяем это поддерево с корнем и делаем из них сбалансированное АВЛ-дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (рис. 2). Так как это первая ситуация, в которой корень рассматриваемого поддерева был &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; становится &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Далее по сохраненной ссылке спускаемся в правое поддерево. Его корень &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Следовательно, строим из него и его правого поддерева &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и спускаемся в левое поддерево. Снова корень &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Строим новое &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; и объединяем его с уже существующим &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (рис. 3).&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:АВВЛ2.jpg|thumb|left|525px|Рис. 2. Создание T'.]]&lt;br /&gt;
|}&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL3.jpg|thumb|left|1050px|Рис. 3. Объединение T' и T1.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Далее действуем по алгоритму и в итоге получаем (рис. 4):&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:End.jpg|thumb|left|525px|Рис. 4. АВЛ-деревья после разделения.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм имеет сложность &amp;lt;tex&amp;gt;O(\log^{2} n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
====Алгоритм второй====&lt;br /&gt;
Рассмотрим решение, которое имеет сложность &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Вернемся к примеру (рис. 1). Теперь рекурсивно спустимся вниз и оттуда будем строить деревья &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;, передавая наверх корректные АВЛ-деревья. То есть для рис. 1 первым в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; придет вершина &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt; с левым поддеревом (выделено светло-зеленым цветом), так как это корректное АВЛ-дерево, оно же и вернется из рекурсии. Далее мы попадем в вершину со значением &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt; и должны слить ее и ее левое поддерево (выделено светло-синим) с тем, что нам пришло. И сделать это нужно так, чтобы передать наверх корректное АВЛ-дерево. Будем действовать по такому алгоритму, пока не дойдем до вершины.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; с корнем &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Тогда сольем его с уже построенным на тот момент &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (&amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; пришло снизу, а значит по условию рекурсии это корректное АВЛ-дерево, &amp;lt;tex&amp;gt;S \leqslant T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(T_{1}) \leqslant h(S)&amp;lt;/tex&amp;gt;). Но так как обычная процедура слияния сливает два АВЛ-дерева, а &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не является корректным АВЛ-деревом, мы немного ее изменим. Пусть мы в дереве &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; нашли самое правое поддерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, высота которого равна высоте &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда сделаем новое дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, корнем которого будет вершина &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (без нее это дерево является сбалансированным), правым поддеревом {{---}} &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, левым {{---}} &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. И подвесим &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; на то место, где мы остановились при поиске &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. Запустим балансировку. В случае, когда корень поддерева, в которое мы пришли, &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, все аналогично.&lt;br /&gt;
&lt;br /&gt;
Разберем пример на рис. 1. Пусть мы рекурсивно спустились до узла &amp;lt;tex&amp;gt;77&amp;lt;/tex&amp;gt;. Ключ больше &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, поэтому эта вершина станет деревом &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и передастся наверх. Теперь мы поднялись в узел &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом станет деревом &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и мы снова поднимемся наверх в узел &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом снова должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, и так как теперь дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; уже не пустое, то их надо слить. После слияния по описанному выше алгоритму получим (рис. 5)&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex.jpg|thumb|left|525px|Рис. 5.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После мы поднимемся в вершину с ключом &amp;lt;tex&amp;gt;80&amp;lt;/tex&amp;gt;. Она с правым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; (рис. 6). &lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex2am.jpg|thumb|left|525px|Рис. 6.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
И на последней итерации мы поднимемся в корень дерева с ключом &amp;lt;tex&amp;gt;50&amp;lt;/tex&amp;gt;, он с левым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, после чего алгоритм завершится.&lt;br /&gt;
&lt;br /&gt;
Пусть поддеревьев с ключами &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt; оказалось больше, чем поддеревьев с ключами &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Докажем для них логарифмическую асимптотику. Дерево на последнем уровне имеет высоту &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; (она может быть не равна &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, если мы придём в &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;). Его мы передаем наверх и вставляем в поддерево высотой &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;H_{k} \leqslant H_{k-1}&amp;lt;/tex&amp;gt;, так как разница высот поддеревьев у любой вершины не больше &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, и мы при переходе от &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt; поднимаемся как минимум на одну вершину вверх. Слияние этих поддеревьев мы выполним за &amp;lt;tex&amp;gt;H_{k-1} - H_{k}&amp;lt;/tex&amp;gt;, получим в итоге дерево высоты не большей, чем &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. Его мы передадим наверх, поэтому в следующий раз слияние будет выполнено за &amp;lt;tex&amp;gt;H_{k-2} - H_{k - 1}&amp;lt;/tex&amp;gt; и так далее. Таким образом мы получим &amp;lt;tex&amp;gt;(H - H_{1}) + (H_{1} - H_{2}) + (H_{2} - H_{3}) + \cdots +  (H_{k - 1} - H_{k}) = H - H_{k} = O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итоговая асимптотика алгоритма {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Splay-дерево]]&lt;br /&gt;
* [[Красно-черное дерево]]&lt;br /&gt;
* [[2-3 дерево]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [http://habrahabr.ru/post/150732/ Habrahabr {{---}} АВЛ-деревья]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;br /&gt;
[[Категория:Структуры данных]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47624</id>
		<title>АВЛ-дерево</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47624"/>
				<updated>2015-06-05T16:42:32Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: /* Удаление вершины */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''АВЛ-дерево''' (англ. ''AVL-Tree'') {{---}} сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.&lt;br /&gt;
&lt;br /&gt;
АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г. М. Адельсона-Вельского и Е. М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962 году.&lt;br /&gt;
&lt;br /&gt;
== Высота дерева ==&lt;br /&gt;
{{Теорема &lt;br /&gt;
|statement=АВЛ-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключами имеет высоту &amp;lt;tex&amp;gt;h = O(\log N)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
||proof=&lt;br /&gt;
&lt;br /&gt;
Высоту поддерева с корнем &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; будем обозначать как &amp;lt;tex&amp;gt;h(x)&amp;lt;/tex&amp;gt;, высоту поддерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; {{---}} как &amp;lt;tex&amp;gt;h(T)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Пусть &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt;, где            &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фибоначчи.  &lt;br /&gt;
|proof=&lt;br /&gt;
Если &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;. Тогда, как легко видеть, &amp;lt;tex&amp;gt;m_{h+2} = m_{h+1} + m_h + 1&amp;lt;/tex&amp;gt;. Равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; докажем по индукции. &lt;br /&gt;
&lt;br /&gt;
База индукции &amp;lt;tex&amp;gt;m_1 = F_3 - 1&amp;lt;/tex&amp;gt; {{---}} верно, &amp;lt;tex&amp;gt;m_1 = 1, F_3 = 2&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Допустим &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} верно. &lt;br /&gt;
&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;m_{h+1} = m_h + m_{h-1} + 1 = F_{h+2} - 1 + F_{h+1} - 1 + 1 = F_{h+3} - 1&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Таким образом, равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} доказано.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\varphi = \dfrac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. То есть&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n \geqslant \varphi^{h}&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 h&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом,  получаем, что высота AVL-дерева из n вершин {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Балансировка ==&lt;br /&gt;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &amp;lt;tex&amp;gt;|h(L) - h(R)| = 2&amp;lt;/tex&amp;gt;, изменяет связи предок-потомок в поддереве данной вершины так, чтобы восстановилось свойство дерева &amp;lt;tex&amp;gt;|h(L) - h(R)| \leqslant 1&amp;lt;/tex&amp;gt;, иначе ничего не меняет.&lt;br /&gt;
Для балансировки будем хранить для каждой вершины разницу между высотой её левого и правого поддерева &amp;lt;tex&amp;gt;diff[i] = h(L) - h(R)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для балансировки вершины используются один из 4 типов вращений:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
 !Тип вращения&lt;br /&gt;
 !Иллюстрация&lt;br /&gt;
 !Когда используется&lt;br /&gt;
 !Расстановка балансов&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое левое вращение''' (''Small left rotation'')&lt;br /&gt;
 |  [[Файл:avl_u1.jpg|2000x200px]]  &lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение''' (англ. Big left rotation'')&lt;br /&gt;
 | [[Файл:avl_u2.jpg|2000x200px]]&lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; , &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |}&lt;br /&gt;
Малый левый поворот:&lt;br /&gt;
    rotateleft(node a) {&lt;br /&gt;
        node b = a.right&lt;br /&gt;
        a.right = b.left&lt;br /&gt;
        b.left = a&lt;br /&gt;
        fixheight(a)&lt;br /&gt;
        fixheight(b)&lt;br /&gt;
    }&lt;br /&gt;
Большой правый поворот пишется проще:&lt;br /&gt;
    bigrotateleft(node a) {&lt;br /&gt;
        rotateright(a.right)&lt;br /&gt;
        rotateleft(a)&lt;br /&gt;
    }&lt;br /&gt;
Малое правое и большое правое вращение определяются симметрично малому левому и большому левому вращению.&lt;br /&gt;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; и не может увеличиться.&lt;br /&gt;
&lt;br /&gt;
Все операции вращения, очевидно, требуют &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
&lt;br /&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;. Если мы стоим в вершине &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и нам надо идти в поддерево, которого нет, то делаем ключ &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; листом, а вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; его корнем. Дальше поднимаемся вверх по пути поиска и пересчитываем баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
Так как в процессе добавления вершины мы рассматриваем не более, чем &amp;lt;tex&amp;gt; O(h) &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;
Если вершина {{---}} лист, то [[Дерево поиска, наивная реализация#Удаление|удалим]] её, иначе найдём самую близкую по значению вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, переместим её на место удаляемой вершины и [[Дерево поиска, наивная реализация#Удаление|удалим]] вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. От удалённой вершины будем подниматься вверх к корню и пересчитывать баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; уменьшается на единицу, если из правого, то увеличивается на единицу. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
В результате указанных действий на удаление вершины и балансировку суммарно тратится, как и ранее, &amp;lt;tex&amp;gt; O(h) &amp;lt;/tex&amp;gt; операций. Таким образом, требуемое количество действий {{---}} &amp;lt;tex&amp;gt; O(\log{n}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Поиск вершины, минимум/максимум в дереве, etc. ===&lt;br /&gt;
Остальные операции не меняют структуры дерева, поэтому выполняются так же, как и в [[Дерево поиска, наивная реализация|наивной реализации]] дерева поиска.&lt;br /&gt;
===Слияние двух AVL-деревьев===&lt;br /&gt;
&lt;br /&gt;
Дано два дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, все ключи в &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;h(T_1) \leqslant h(T_2)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В дереве &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; удаляем самую правую вершину, назовём её &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;. Высота дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; может уменьшиться на единицу. В дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; идём от корня всегда в левое поддерево и, когда высота этого поддерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; будет равна высоте дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, делаем новое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; будет вершина &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, левым поддеревом будет дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, а правым дерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;. Теперь в дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; у вершины, в которой мы остановились при спуске, левым поддеревом делаем дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; до слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u3.jpg|340px]]&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; после слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u4.jpg|300px]]&lt;br /&gt;
&lt;br /&gt;
===Алгоритм разделения AVL-дерева на два===&lt;br /&gt;
====Алгоритм первый====&lt;br /&gt;
Пусть у нас есть дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Мы должны разбить его на два дерева &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; такие, что &amp;lt;tex&amp;gt;T_{1} \leqslant x&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;x &amp;lt; T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Предположим, что корень нашего дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, в таком случае все левое поддерево вместе с корнем после разделения отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда рекурсивно спускаемся в правое поддерево и там проверяем это условие (так как часть правого поддерева тоже может содержать ключи &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;). Если же корень оказался &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, то мы спускаемся той же рекурсией, но только в левое поддерево и ищем там.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. В таком случае этот корень со своим левым поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Поэтому мы делаем следующее: запоминаем ссылку на правое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, удаляем корень, запоминая его значение (не меняя конфигурацию дерева, то есть просто делаем ссылки на него NULL'ами). Таким образом, мы отделяем сбалансированное АВЛ-дерево (бывшее левое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Делаем новую вершину со значением бывшего корня правым листом самой правой вершины &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Обозначим полученное дерево за &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;. Теперь нам нужно объединить его с уже построенным ранее &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (оно может быть пустым, если мы первый раз нашли такое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Для этого мы ищем в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; самое правое поддерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; высоты, равной высоте &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (спускаясь от корня всегда в правые поддеревья). Делаем новое дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, сливая &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (очевидно, все ключи в &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, поэтому мы можем это сделать). Теперь в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; у отца вершины, в которой мы остановились при поиске дерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;, правым поддеревом делаем дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt; и запускаем балансировку. После нужно спуститься в правое поддерево бывшего дерева &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (по ссылке, которую мы ранее запомнили) и обработать его.&lt;br /&gt;
&lt;br /&gt;
Если мы пришли в поддерево &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, совершаем аналогичные действия: делаем NULL'ами ссылки на корень &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, запоминая ссылку на его левое поддерево. Делаем новую вершину со значением бывшего корня левым листом самой левой вершины &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt; и запускаем балансировку. Объединяем полученное АВЛ-дерево с уже построенным ранее &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; аналогичным первому случаю способом, только теперь мы ищем самое левое поддерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рассмотри пример (рис. 1). Цветом выделены поддеревья, которые после разделения должны отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;x = 76&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL.jpg|thumb|left|525px|Рис. 1. Разделение АВЛ-дерева на два.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Корень дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, поэтому он со всем выделенным поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. По описанному выше алгоритму отделяем это поддерево с корнем и делаем из них сбалансированное АВЛ-дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (рис. 2). Так как это первая ситуация, в которой корень рассматриваемого поддерева был &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; становится &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Далее по сохраненной ссылке спускаемся в правое поддерево. Его корень &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Следовательно, строим из него и его правого поддерева &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и спускаемся в левое поддерево. Снова корень &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Строим новое &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; и объединяем его с уже существующим &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (рис. 3).&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:АВВЛ2.jpg|thumb|left|525px|Рис. 2. Создание T'.]]&lt;br /&gt;
|}&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL3.jpg|thumb|left|1050px|Рис. 3. Объединение T' и T1.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Далее действуем по алгоритму и в итоге получаем (рис. 4):&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:End.jpg|thumb|left|525px|Рис. 4. АВЛ-деревья после разделения.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм имеет сложность &amp;lt;tex&amp;gt;O(\log^{2} n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
====Алгоритм второй====&lt;br /&gt;
Рассмотрим решение, которое имеет сложность &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Вернемся к примеру (рис. 1). Теперь рекурсивно спустимся вниз и оттуда будем строить деревья &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;, передавая наверх корректные АВЛ-деревья. То есть для рис. 1 первым в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; придет вершина &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt; с левым поддеревом (выделено светло-зеленым цветом), так как это корректное АВЛ-дерево, оно же и вернется из рекурсии. Далее мы попадем в вершину со значением &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt; и должны слить ее и ее левое поддерево (выделено светло-синим) с тем, что нам пришло. И сделать это нужно так, чтобы передать наверх корректное АВЛ-дерево. Будем действовать по такому алгоритму, пока не дойдем до вершины.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; с корнем &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Тогда сольем его с уже построенным на тот момент &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (&amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; пришло снизу, а значит по условию рекурсии это корректное АВЛ-дерево, &amp;lt;tex&amp;gt;S \leqslant T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(T_{1}) \leqslant h(S)&amp;lt;/tex&amp;gt;). Но так как обычная процедура слияния сливает два АВЛ-дерева, а &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не является корректным АВЛ-деревом, мы немного ее изменим. Пусть мы в дереве &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; нашли самое правое поддерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, высота которого равна высоте &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда сделаем новое дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, корнем которого будет вершина &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (без нее это дерево является сбалансированным), правым поддеревом {{---}} &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, левым {{---}} &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. И подвесим &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; на то место, где мы остановились при поиске &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. Запустим балансировку. В случае, когда корень поддерева, в которое мы пришли, &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, все аналогично.&lt;br /&gt;
&lt;br /&gt;
Разберем пример на рис. 1. Пусть мы рекурсивно спустились до узла &amp;lt;tex&amp;gt;77&amp;lt;/tex&amp;gt;. Ключ больше &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, поэтому эта вершина станет деревом &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и передастся наверх. Теперь мы поднялись в узел &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом станет деревом &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и мы снова поднимемся наверх в узел &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом снова должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, и так как теперь дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; уже не пустое, то их надо слить. После слияния по описанному выше алгоритму получим (рис. 5)&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex.jpg|thumb|left|525px|Рис. 5.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После мы поднимемся в вершину с ключом &amp;lt;tex&amp;gt;80&amp;lt;/tex&amp;gt;. Она с правым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; (рис. 6). &lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex2am.jpg|thumb|left|525px|Рис. 6.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
И на последней итерации мы поднимемся в корень дерева с ключом &amp;lt;tex&amp;gt;50&amp;lt;/tex&amp;gt;, он с левым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, после чего алгоритм завершится.&lt;br /&gt;
&lt;br /&gt;
Пусть поддеревьев с ключами &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt; оказалось больше, чем поддеревьев с ключами &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Докажем для них логарифмическую асимптотику. Дерево на последнем уровне имеет высоту &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; (она может быть не равна &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, если мы придём в &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;). Его мы передаем наверх и вставляем в поддерево высотой &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;H_{k} \leqslant H_{k-1}&amp;lt;/tex&amp;gt;, так как разница высот поддеревьев у любой вершины не больше &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, и мы при переходе от &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt; поднимаемся как минимум на одну вершину вверх. Слияние этих поддеревьев мы выполним за &amp;lt;tex&amp;gt;H_{k-1} - H_{k}&amp;lt;/tex&amp;gt;, получим в итоге дерево высоты не большей, чем &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. Его мы передадим наверх, поэтому в следующий раз слияние будет выполнено за &amp;lt;tex&amp;gt;H_{k-2} - H_{k - 1}&amp;lt;/tex&amp;gt; и так далее. Таким образом мы получим &amp;lt;tex&amp;gt;(H - H_{1}) + (H_{1} - H_{2}) + (H_{2} - H_{3}) + \cdots +  (H_{k - 1} - H_{k}) = H - H_{k} = O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итоговая асимптотика алгоритма {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [http://habrahabr.ru/post/150732/ Habrahabr {{---}} АВЛ-деревья]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;br /&gt;
[[Категория:Структуры данных]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47622</id>
		<title>АВЛ-дерево</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47622"/>
				<updated>2015-06-05T16:26:12Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''АВЛ-дерево''' (англ. ''AVL-Tree'') {{---}} сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.&lt;br /&gt;
&lt;br /&gt;
АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г. М. Адельсона-Вельского и Е. М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962 году.&lt;br /&gt;
&lt;br /&gt;
== Высота дерева ==&lt;br /&gt;
{{Теорема &lt;br /&gt;
|statement=АВЛ-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключами имеет высоту &amp;lt;tex&amp;gt;h = O(\log N)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
||proof=&lt;br /&gt;
&lt;br /&gt;
Высоту поддерева с корнем &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; будем обозначать как &amp;lt;tex&amp;gt;h(x)&amp;lt;/tex&amp;gt;, высоту поддерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; {{---}} как &amp;lt;tex&amp;gt;h(T)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Пусть &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt;, где            &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фибоначчи.  &lt;br /&gt;
|proof=&lt;br /&gt;
Если &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;. Тогда, как легко видеть, &amp;lt;tex&amp;gt;m_{h+2} = m_{h+1} + m_h + 1&amp;lt;/tex&amp;gt;. Равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; докажем по индукции. &lt;br /&gt;
&lt;br /&gt;
База индукции &amp;lt;tex&amp;gt;m_1 = F_3 - 1&amp;lt;/tex&amp;gt; {{---}} верно, &amp;lt;tex&amp;gt;m_1 = 1, F_3 = 2&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Допустим &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} верно. &lt;br /&gt;
&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;m_{h+1} = m_h + m_{h-1} + 1 = F_{h+2} - 1 + F_{h+1} - 1 + 1 = F_{h+3} - 1&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Таким образом, равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} доказано.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\varphi = \dfrac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. То есть&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n \geqslant \varphi^{h}&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 h&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом,  получаем, что высота AVL-дерева из n вершин {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Балансировка ==&lt;br /&gt;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &amp;lt;tex&amp;gt;|h(L) - h(R)| = 2&amp;lt;/tex&amp;gt;, изменяет связи предок-потомок в поддереве данной вершины так, чтобы восстановилось свойство дерева &amp;lt;tex&amp;gt;|h(L) - h(R)| \leqslant 1&amp;lt;/tex&amp;gt;, иначе ничего не меняет.&lt;br /&gt;
Для балансировки будем хранить для каждой вершины разницу между высотой её левого и правого поддерева &amp;lt;tex&amp;gt;diff[i] = h(L) - h(R)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для балансировки вершины используются один из 4 типов вращений:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
 !Тип вращения&lt;br /&gt;
 !Иллюстрация&lt;br /&gt;
 !Когда используется&lt;br /&gt;
 !Расстановка балансов&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое левое вращение''' (''Small left rotation'')&lt;br /&gt;
 |  [[Файл:avl_u1.jpg|2000x200px]]  &lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение''' (англ. Big left rotation'')&lt;br /&gt;
 | [[Файл:avl_u2.jpg|2000x200px]]&lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; , &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |}&lt;br /&gt;
Малый левый поворот:&lt;br /&gt;
    rotateleft(node a) {&lt;br /&gt;
        node b = a.right&lt;br /&gt;
        a.right = b.left&lt;br /&gt;
        b.left = a&lt;br /&gt;
        fixheight(a)&lt;br /&gt;
        fixheight(b)&lt;br /&gt;
    }&lt;br /&gt;
Большой правый поворот пишется проще:&lt;br /&gt;
    bigrotateleft(node a) {&lt;br /&gt;
        rotateright(a.right)&lt;br /&gt;
        rotateleft(a)&lt;br /&gt;
    }&lt;br /&gt;
Малое правое и большое правое вращение определяются симметрично малому левому и большому левому вращению.&lt;br /&gt;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; и не может увеличиться.&lt;br /&gt;
&lt;br /&gt;
Все операции вращения, очевидно, требуют &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
&lt;br /&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;. Если мы стоим в вершине &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и нам надо идти в поддерево, которого нет, то делаем ключ &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; листом, а вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; его корнем. Дальше поднимаемся вверх по пути поиска и пересчитываем баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
Так как в процессе добавления вершины мы рассматриваем не более, чем &amp;lt;tex&amp;gt; O(h) &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;
Если вершина - лист, то [[Дерево поиска, наивная реализация#Удаление|удалим]] её, иначе найдём самую близкую по значению вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, переместим её на место удаляемой вершины и [[Дерево поиска, наивная реализация#Удаление|удалим]] вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. От удалённой вершины будем подниматься вверх к корню и пересчитывать баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; уменьшается на единицу, если из правого, то увеличивается на единицу. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
В результате указанных действий на удаление вершины и балансировку суммарно тратится, как и ранее, &amp;lt;tex&amp;gt; O(h) &amp;lt;/tex&amp;gt; операций. Таким образом, требуемое количество действий {{---}} &amp;lt;tex&amp;gt; O(\log{n}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Поиск вершины, минимум/максимум в дереве, etc. ===&lt;br /&gt;
Остальные операции не меняют структуры дерева, поэтому выполняются так же, как и в [[Дерево поиска, наивная реализация|наивной реализации]] дерева поиска.&lt;br /&gt;
===Слияние двух AVL-деревьев===&lt;br /&gt;
&lt;br /&gt;
Дано два дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, все ключи в &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;h(T_1) \leqslant h(T_2)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В дереве &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; удаляем самую правую вершину, назовём её &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;. Высота дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; может уменьшиться на единицу. В дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; идём от корня всегда в левое поддерево и, когда высота этого поддерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; будет равна высоте дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, делаем новое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; будет вершина &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, левым поддеревом будет дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, а правым дерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;. Теперь в дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; у вершины, в которой мы остановились при спуске, левым поддеревом делаем дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; до слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u3.jpg|340px]]&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; после слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u4.jpg|300px]]&lt;br /&gt;
&lt;br /&gt;
===Алгоритм разделения AVL-дерева на два===&lt;br /&gt;
====Алгоритм первый====&lt;br /&gt;
Пусть у нас есть дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Мы должны разбить его на два дерева &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; такие, что &amp;lt;tex&amp;gt;T_{1} \leqslant x&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;x &amp;lt; T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Предположим, что корень нашего дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, в таком случае все левое поддерево вместе с корнем после разделения отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда рекурсивно спускаемся в правое поддерево и там проверяем это условие (так как часть правого поддерева тоже может содержать ключи &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;). Если же корень оказался &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, то мы спускаемся той же рекурсией, но только в левое поддерево и ищем там.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. В таком случае этот корень со своим левым поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Поэтому мы делаем следующее: запоминаем ссылку на правое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, удаляем корень, запоминая его значение (не меняя конфигурацию дерева, то есть просто делаем ссылки на него NULL'ами). Таким образом, мы отделяем сбалансированное АВЛ-дерево (бывшее левое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Делаем новую вершину со значением бывшего корня правым листом самой правой вершины &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Обозначим полученное дерево за &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;. Теперь нам нужно объединить его с уже построенным ранее &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (оно может быть пустым, если мы первый раз нашли такое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Для этого мы ищем в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; самое правое поддерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; высоты, равной высоте &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (спускаясь от корня всегда в правые поддеревья). Делаем новое дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, сливая &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (очевидно, все ключи в &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, поэтому мы можем это сделать). Теперь в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; у отца вершины, в которой мы остановились при поиске дерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;, правым поддеревом делаем дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt; и запускаем балансировку. После нужно спуститься в правое поддерево бывшего дерева &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (по ссылке, которую мы ранее запомнили) и обработать его.&lt;br /&gt;
&lt;br /&gt;
Если мы пришли в поддерево &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, совершаем аналогичные действия: делаем NULL'ами ссылки на корень &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, запоминая ссылку на его левое поддерево. Делаем новую вершину со значением бывшего корня левым листом самой левой вершины &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt; и запускаем балансировку. Объединяем полученное АВЛ-дерево с уже построенным ранее &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; аналогичным первому случаю способом, только теперь мы ищем самое левое поддерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рассмотри пример (рис. 1). Цветом выделены поддеревья, которые после разделения должны отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;x = 76&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL.jpg|thumb|left|525px|Рис. 1. Разделение АВЛ-дерева на два.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Корень дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, поэтому он со всем выделенным поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. По описанному выше алгоритму отделяем это поддерево с корнем и делаем из них сбалансированное АВЛ-дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (рис. 2). Так как это первая ситуация, в которой корень рассматриваемого поддерева был &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; становится &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Далее по сохраненной ссылке спускаемся в правое поддерево. Его корень &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Следовательно, строим из него и его правого поддерева &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и спускаемся в левое поддерево. Снова корень &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Строим новое &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; и объединяем его с уже существующим &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (рис. 3).&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:АВВЛ2.jpg|thumb|left|525px|Рис. 2. Создание T'.]]&lt;br /&gt;
|}&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL3.jpg|thumb|left|1050px|Рис. 3. Объединение T' и T1.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Далее действуем по алгоритму и в итоге получаем (рис. 4):&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:End.jpg|thumb|left|525px|Рис. 4. АВЛ-деревья после разделения.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм имеет сложность &amp;lt;tex&amp;gt;O(\log^{2} n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
====Алгоритм второй====&lt;br /&gt;
Рассмотрим решение, которое имеет сложность &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Вернемся к примеру (рис. 1). Теперь рекурсивно спустимся вниз и оттуда будем строить деревья &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;, передавая наверх корректные АВЛ-деревья. То есть для рис. 1 первым в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; придет вершина &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt; с левым поддеревом (выделено светло-зеленым цветом), так как это корректное АВЛ-дерево, оно же и вернется из рекурсии. Далее мы попадем в вершину со значением &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt; и должны слить ее и ее левое поддерево (выделено светло-синим) с тем, что нам пришло. И сделать это нужно так, чтобы передать наверх корректное АВЛ-дерево. Будем действовать по такому алгоритму, пока не дойдем до вершины.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; с корнем &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Тогда сольем его с уже построенным на тот момент &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (&amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; пришло снизу, а значит по условию рекурсии это корректное АВЛ-дерево, &amp;lt;tex&amp;gt;S \leqslant T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(T_{1}) \leqslant h(S)&amp;lt;/tex&amp;gt;). Но так как обычная процедура слияния сливает два АВЛ-дерева, а &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не является корректным АВЛ-деревом, мы немного ее изменим. Пусть мы в дереве &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; нашли самое правое поддерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, высота которого равна высоте &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда сделаем новое дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, корнем которого будет вершина &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (без нее это дерево является сбалансированным), правым поддеревом {{---}} &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, левым {{---}} &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. И подвесим &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; на то место, где мы остановились при поиске &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. Запустим балансировку. В случае, когда корень поддерева, в которое мы пришли, &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, все аналогично.&lt;br /&gt;
&lt;br /&gt;
Разберем пример на рис. 1. Пусть мы рекурсивно спустились до узла &amp;lt;tex&amp;gt;77&amp;lt;/tex&amp;gt;. Ключ больше &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, поэтому эта вершина станет деревом &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и передастся наверх. Теперь мы поднялись в узел &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом станет деревом &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и мы снова поднимемся наверх в узел &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом снова должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, и так как теперь дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; уже не пустое, то их надо слить. После слияния по описанному выше алгоритму получим (рис. 5)&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex.jpg|thumb|left|525px|Рис. 5.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После мы поднимемся в вершину с ключом &amp;lt;tex&amp;gt;80&amp;lt;/tex&amp;gt;. Она с правым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; (рис. 6). &lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex2am.jpg|thumb|left|525px|Рис. 6.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
И на последней итерации мы поднимемся в корень дерева с ключом &amp;lt;tex&amp;gt;50&amp;lt;/tex&amp;gt;, он с левым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, после чего алгоритм завершится.&lt;br /&gt;
&lt;br /&gt;
Пусть поддеревьев с ключами &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt; оказалось больше, чем поддеревьев с ключами &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Докажем для них логарифмическую асимптотику. Дерево на последнем уровне имеет высоту &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; (она может быть не равна &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, если мы придём в &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;). Его мы передаем наверх и вставляем в поддерево высотой &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;H_{k} \leqslant H_{k-1}&amp;lt;/tex&amp;gt;, так как разница высот поддеревьев у любой вершины не больше &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, и мы при переходе от &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt; поднимаемся как минимум на одну вершину вверх. Слияние этих поддеревьев мы выполним за &amp;lt;tex&amp;gt;H_{k-1} - H_{k}&amp;lt;/tex&amp;gt;, получим в итоге дерево высоты не большей, чем &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. Его мы передадим наверх, поэтому в следующий раз слияние будет выполнено за &amp;lt;tex&amp;gt;H_{k-2} - H_{k - 1}&amp;lt;/tex&amp;gt; и так далее. Таким образом мы получим &amp;lt;tex&amp;gt;(H - H_{1}) + (H_{1} - H_{2}) + (H_{2} - H_{3}) + \cdots +  (H_{k - 1} - H_{k}) = H - H_{k} = O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итоговая асимптотика алгоритма {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [http://habrahabr.ru/post/150732/ Habrahabr {{---}} АВЛ-деревья]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;br /&gt;
[[Категория:Структуры данных]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47621</id>
		<title>АВЛ-дерево</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47621"/>
				<updated>2015-06-05T16:24:01Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''АВЛ-дерево''' (англ. ''AVL-Tree'') {{---}} сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.&lt;br /&gt;
&lt;br /&gt;
АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г. М. Адельсона-Вельского и Е. М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962 году.&lt;br /&gt;
&lt;br /&gt;
== Высота дерева ==&lt;br /&gt;
{{Теорема &lt;br /&gt;
|statement=АВЛ-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключами имеет высоту &amp;lt;tex&amp;gt;h = O(\log N)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
||proof=&lt;br /&gt;
&lt;br /&gt;
Высоту поддерева с корнем &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; будем обозначать как &amp;lt;tex&amp;gt;h(x)&amp;lt;/tex&amp;gt;, высоту поддерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; {{---}} как &amp;lt;tex&amp;gt;h(T)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Пусть &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt;, где            &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фибоначчи.  &lt;br /&gt;
|proof=&lt;br /&gt;
Если &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;. Тогда, как легко видеть, &amp;lt;tex&amp;gt;m_{h+2} = m_{h+1} + m_h + 1&amp;lt;/tex&amp;gt;. Равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; докажем по индукции. &lt;br /&gt;
&lt;br /&gt;
База индукции &amp;lt;tex&amp;gt;m_1 = F_3 - 1&amp;lt;/tex&amp;gt; {{---}} верно, &amp;lt;tex&amp;gt;m_1 = 1, F_3 = 2&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Допустим &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} верно. &lt;br /&gt;
&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;m_{h+1} = m_h + m_{h-1} + 1 = F_{h+2} - 1 + F_{h+1} - 1 + 1 = F_{h+3} - 1&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Таким образом, равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} доказано.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\varphi = \dfrac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. То есть&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n \geqslant \varphi^{h}&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 h&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом,  получаем, что высота AVL-дерева из n вершин {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Балансировка ==&lt;br /&gt;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &amp;lt;tex&amp;gt;|h(L) - h(R)| = 2&amp;lt;/tex&amp;gt;, изменяет связи предок-потомок в поддереве данной вершины так, чтобы восстановилось свойство дерева &amp;lt;tex&amp;gt;|h(L) - h(R)| \leqslant 1&amp;lt;/tex&amp;gt;, иначе ничего не меняет.&lt;br /&gt;
Для балансировки будем хранить для каждой вершины разницу между высотой её левого и правого поддерева &amp;lt;tex&amp;gt;diff[i] = h(L) - h(R)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для балансировки вершины используются один из 4 типов вращений:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
 !Тип вращения&lt;br /&gt;
 !Иллюстрация&lt;br /&gt;
 !Когда используется&lt;br /&gt;
 !Расстановка балансов&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое левое вращение''' (''Small left rotation'')&lt;br /&gt;
 |  [[Файл:avl_u1.jpg|2000x200px]]  &lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение''' (англ. Big left rotation'')&lt;br /&gt;
 | [[Файл:avl_u2.jpg|2000x200px]]&lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; , &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |}&lt;br /&gt;
Малый левый поворот:&lt;br /&gt;
    rotateleft(node a) {&lt;br /&gt;
        node b = a.right&lt;br /&gt;
        a.right = b.left&lt;br /&gt;
        b.left = a&lt;br /&gt;
        fixheight(a)&lt;br /&gt;
        fixheight(b)&lt;br /&gt;
    }&lt;br /&gt;
Большой правый поворот пишется проще:&lt;br /&gt;
    bigrotateleft(node a) {&lt;br /&gt;
        rotateright(a.right)&lt;br /&gt;
        rotateleft(a)&lt;br /&gt;
    }&lt;br /&gt;
Малое правое и большое правое вращение определяются симметрично малому левому и большому левому вращению.&lt;br /&gt;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; и не может увеличиться.&lt;br /&gt;
&lt;br /&gt;
Все операции вращения, очевидно, требуют &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
&lt;br /&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;. Если мы стоим в вершине &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и нам надо идти в поддерево, которого нет, то делаем ключ &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; листом, а вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; его корнем. Дальше поднимаемся вверх по пути поиска и пересчитываем баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
Так как в процессе добавления вершины мы рассматриваем не более, чем &amp;lt;tex&amp;gt; O(h) &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;
Если вершина - лист, то [[Дерево поиска, наивная реализация#Удаление|удалим]] её, иначе найдём самую близкую по значению вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, переместим её на место удаляемой вершины и [[Дерево поиска, наивная реализация#Удаление|удалим]] вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. От удалённой вершины будем подниматься вверх к корню и пересчитывать баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; уменьшается на единицу, если из правого, то увеличивается на единицу. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
В результате указанных действий на удаление вершины и балансировку суммарно тратится, как и ранее, &amp;lt;tex&amp;gt; O(h) &amp;lt;/tex&amp;gt; операций. Таким образом, требуемое количество действий {{---}} &amp;lt;tex&amp;gt; O(\log{n}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Поиск вершины, минимум/максимум в дереве, etc. ===&lt;br /&gt;
Остальные операции не меняют структуры дерева, поэтому выполняются так же, как и в [[Дерево поиска, наивная реализация|наивной реализации]] дерева поиска.&lt;br /&gt;
===Слияние двух AVL-деревьев===&lt;br /&gt;
&lt;br /&gt;
Дано два дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, все ключи в &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;h(T_1) \leqslant h(T_2)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В дереве &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; удаляем самую правую вершину, назовём её &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;. Высота дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; может уменьшиться на единицу. В дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; идём от корня всегда в левое поддерево и, когда высота этого поддерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; будет равна высоте дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, делаем новое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; будет вершина &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, левым поддеревом будет дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, а правым дерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;. Теперь в дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; у вершины, в которой мы остановились при спуске, левым поддеревом делаем дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; до слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u3.jpg|340px]]&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; после слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u4.jpg|300px]]&lt;br /&gt;
&lt;br /&gt;
===Алгоритм разделения AVL-дерева на два===&lt;br /&gt;
====Алгоритм первый====&lt;br /&gt;
Пусть у нас есть дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Мы должны разбить его на два дерева &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; такие, что &amp;lt;tex&amp;gt;T_{1} \leqslant x&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;x &amp;lt; T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Предположим, что корень нашего дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, в таком случае все левое поддерево вместе с корнем после разделения отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда рекурсивно спускаемся в правое поддерево и там проверяем это условие (так как часть правого поддерева тоже может содержать ключи &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;). Если же корень оказался &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, то мы спускаемся той же рекурсией, но только в левое поддерево и ищем там.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. В таком случае этот корень со своим левым поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Поэтому мы делаем следующее: запоминаем ссылку на правое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, удаляем корень, запоминая его значение (не меняя конфигурацию дерева, то есть просто делаем ссылки на него NULL'ами). Таким образом, мы отделяем сбалансированное АВЛ-дерево (бывшее левое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Делаем новую вершину со значением бывшего корня правым листом самой правой вершины &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Обозначим полученное дерево за &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;. Теперь нам нужно объединить его с уже построенным ранее &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (оно может быть пустым, если мы первый раз нашли такое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Для этого мы ищем в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; самое правое поддерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; высоты, равной высоте &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (спускаясь от корня всегда в правые поддеревья). Делаем новое дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, сливая &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (очевидно, все ключи в &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, поэтому мы можем это сделать). Теперь в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; у отца вершины, в которой мы остановились при поиске дерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;, правым поддеревом делаем дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt; и запускаем балансировку. После нужно спуститься в правое поддерево бывшего дерева &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (по ссылке, которую мы ранее запомнили) и обработать его.&lt;br /&gt;
&lt;br /&gt;
Если мы пришли в поддерево &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, совершаем аналогичные действия: делаем NULL'ами ссылки на корень &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, запоминая ссылку на его левое поддерево. Делаем новую вершину со значением бывшего корня левым листом самой левой вершины &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt; и запускаем балансировку. Объединяем полученное АВЛ-дерево с уже построенным ранее &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; аналогичным первому случаю способом, только теперь мы ищем самое левое поддерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рассмотри пример (рис. 1). Цветом выделены поддеревья, которые после разделения должны отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;x = 76&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL.jpg|thumb|left|525px|Рис. 1. Разделение АВЛ-дерева на два.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Корень дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, поэтому он со всем выделенным поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. По описанному выше алгоритму отделяем это поддерево с корнем и делаем из них сбалансированное АВЛ-дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (рис. 2). Так как это первая ситуация, в которой корень рассматриваемого поддерева был &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; становится &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Далее по сохраненной ссылке спускаемся в правое поддерево. Его корень &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Следовательно, строим из него и его правого поддерева &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и спускаемся в левое поддерево. Снова корень &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Строим новое &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; и объединяем его с уже существующим &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (рис. 3).&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:АВВЛ2.jpg|thumb|left|525px|Рис. 2. Создание T'.]]&lt;br /&gt;
|}&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL3.jpg|thumb|left|1050px|Рис. 3. Объединение T' и T1.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Далее действуем по алгоритму и в итоге получаем (рис. 4):&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:End.jpg|thumb|left|525px|Рис. 4. АВЛ-деревья после разделения.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм имеет сложность &amp;lt;tex&amp;gt;O(\log^{2} n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
====Алгоритм второй====&lt;br /&gt;
Рассмотрим решение, которое имеет сложность &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Вернемся к примеру (рис. 1). Теперь рекурсивно спустимся вниз и оттуда будем строить деревья &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;, передавая наверх корректные АВЛ-деревья. То есть для рис. 1 первым в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; придет вершина &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt; с левым поддеревом (выделено светло-зеленым цветом), так как это корректное АВЛ-дерево, оно же и вернется из рекурсии. Далее мы попадем в вершину со значением &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt; и должны слить ее и ее левое поддерево (выделено светло-синим) с тем, что нам пришло. И сделать это нужно так, чтобы передать наверх корректное АВЛ-дерево. Будем действовать по такому алгоритму, пока не дойдем до вершины.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; с корнем &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Тогда сольем его с уже построенным на тот момент &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (&amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; пришло снизу, а значит по условию рекурсии это корректное АВЛ-дерево, &amp;lt;tex&amp;gt;S \leqslant T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(T_{1}) \leqslant h(S)&amp;lt;/tex&amp;gt;). Но так как обычная процедура слияния сливает два АВЛ-дерева, а &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не является корректным АВЛ-деревом, мы немного ее изменим. Пусть мы в дереве &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; нашли самое правое поддерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, высота которого равна высоте &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда сделаем новое дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, корнем которого будет вершина &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (без нее это дерево является сбалансированным), правым поддеревом {{---}} &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, левым {{---}} &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. И подвесим &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; на то место, где мы остановились при поиске &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. Запустим балансировку. В случае, когда корень поддерева, в которое мы пришли, &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, все аналогично.&lt;br /&gt;
&lt;br /&gt;
Разберем пример на рис. 1. Пусть мы рекурсивно спустились до узла &amp;lt;tex&amp;gt;77&amp;lt;/tex&amp;gt;. Ключ больше &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, поэтому эта вершина станет деревом &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и передастся наверх. Теперь мы поднялись в узел &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом станет деревом &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и мы снова поднимемся наверх в узел &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом снова должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, и так как теперь дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; уже не пустое, то их надо слить. После слияния по описанному выше алгоритму получим (рис. 5)&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex.jpg|thumb|left|525px|Рис. 5.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После мы поднимемся в вершину с ключом &amp;lt;tex&amp;gt;80&amp;lt;/tex&amp;gt;. Она с правым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; (рис. 6). &lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex2am.jpg|thumb|left|525px|Рис. 6.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
И на последней итерации мы поднимемся в корень дерева с ключом &amp;lt;tex&amp;gt;50&amp;lt;/tex&amp;gt;, он с левым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, после чего алгоритм завершится.&lt;br /&gt;
&lt;br /&gt;
Пусть поддеревьев с ключами &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt; оказалось больше, чем поддеревьев с ключами &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Докажем для них логарифмическую асимптотику. Дерево на последнем уровне имеет высоту &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; (она может быть не равна &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, если мы придём в &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;). Его мы передаем наверх и вставляем в поддерево высотой &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;H_{k} \leqslant H_{k-1}&amp;lt;/tex&amp;gt;, так как разница высот поддеревьев у любой вершины не больше &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, и мы при переходе от &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt; поднимаемся как минимум на одну вершину вверх. Слияние этих поддеревьев мы выполним за &amp;lt;tex&amp;gt;H_{k-1} - H_{k}&amp;lt;/tex&amp;gt;, получим в итоге дерево высоты не большей, чем &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. Его мы передадим наверх, поэтому в следующий раз слияние будет выполнено за &amp;lt;tex&amp;gt;H_{k-2} - H_{k - 1}&amp;lt;/tex&amp;gt; и так далее. Таким образом мы получим &amp;lt;tex&amp;gt;(H - H_{1}) + (H_{1} - H_{2}) + (H_{2} - H_{3}) + \cdots +  (H_{k - 1} - H_{k}) = H - H_{k} = O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итоговая асимптотика алгоритма {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [http://habrahabr.ru/post/150732/ Habrahabr {{---}} АВЛ-деревья]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47619</id>
		<title>АВЛ-дерево</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47619"/>
				<updated>2015-06-05T16:22:10Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: /* Балансировка */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''АВЛ-дерево''' (''AVL-Tree'') {{---}} сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.&lt;br /&gt;
&lt;br /&gt;
АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г. М. Адельсона-Вельского и Е. М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962 году.&lt;br /&gt;
&lt;br /&gt;
== Высота дерева ==&lt;br /&gt;
{{Теорема &lt;br /&gt;
|statement=АВЛ-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключами имеет высоту &amp;lt;tex&amp;gt;h = O(\log N)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
||proof=&lt;br /&gt;
&lt;br /&gt;
Высоту поддерева с корнем &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; будем обозначать как &amp;lt;tex&amp;gt;h(x)&amp;lt;/tex&amp;gt;, высоту поддерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; {{---}} как &amp;lt;tex&amp;gt;h(T)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Пусть &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt;, где            &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фибоначчи.  &lt;br /&gt;
|proof=&lt;br /&gt;
Если &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;. Тогда, как легко видеть, &amp;lt;tex&amp;gt;m_{h+2} = m_{h+1} + m_h + 1&amp;lt;/tex&amp;gt;. Равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; докажем по индукции. &lt;br /&gt;
&lt;br /&gt;
База индукции &amp;lt;tex&amp;gt;m_1 = F_3 - 1&amp;lt;/tex&amp;gt; {{---}} верно, &amp;lt;tex&amp;gt;m_1 = 1, F_3 = 2&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Допустим &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} верно. &lt;br /&gt;
&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;m_{h+1} = m_h + m_{h-1} + 1 = F_{h+2} - 1 + F_{h+1} - 1 + 1 = F_{h+3} - 1&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Таким образом, равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} доказано.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\varphi = \dfrac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. То есть&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n \geqslant \varphi^{h}&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 h&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом,  получаем, что высота AVL-дерева из n вершин {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Балансировка ==&lt;br /&gt;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &amp;lt;tex&amp;gt;|h(L) - h(R)| = 2&amp;lt;/tex&amp;gt;, изменяет связи предок-потомок в поддереве данной вершины так, чтобы восстановилось свойство дерева &amp;lt;tex&amp;gt;|h(L) - h(R)| \leqslant 1&amp;lt;/tex&amp;gt;, иначе ничего не меняет.&lt;br /&gt;
Для балансировки будем хранить для каждой вершины разницу между высотой её левого и правого поддерева &amp;lt;tex&amp;gt;diff[i] = h(L) - h(R)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для балансировки вершины используются один из 4 типов вращений:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
 !Тип вращения&lt;br /&gt;
 !Иллюстрация&lt;br /&gt;
 !Когда используется&lt;br /&gt;
 !Расстановка балансов&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое левое вращение''' (''Small left rotation'')&lt;br /&gt;
 |  [[Файл:avl_u1.jpg|2000x200px]]  &lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение''' (Big left rotation'')&lt;br /&gt;
 | [[Файл:avl_u2.jpg|2000x200px]]&lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; , &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |}&lt;br /&gt;
Малый левый поворот:&lt;br /&gt;
    rotateleft(node a) {&lt;br /&gt;
        node b = a.right&lt;br /&gt;
        a.right = b.left&lt;br /&gt;
        b.left = a&lt;br /&gt;
        fixheight(a)&lt;br /&gt;
        fixheight(b)&lt;br /&gt;
    }&lt;br /&gt;
Большой правый поворот пишется проще:&lt;br /&gt;
    bigrotateleft(node a) {&lt;br /&gt;
        rotateright(a.right)&lt;br /&gt;
        rotateleft(a)&lt;br /&gt;
    }&lt;br /&gt;
Малое правое и большое правое вращение определяются симметрично малому левому и большому левому вращению.&lt;br /&gt;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; и не может увеличиться.&lt;br /&gt;
&lt;br /&gt;
Все операции вращения, очевидно, требуют &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
&lt;br /&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;. Если мы стоим в вершине &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и нам надо идти в поддерево, которого нет, то делаем ключ &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; листом, а вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; его корнем. Дальше поднимаемся вверх по пути поиска и пересчитываем баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
Так как в процессе добавления вершины мы рассматриваем не более, чем &amp;lt;tex&amp;gt; O(h) &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;
Если вершина - лист, то [[Дерево поиска, наивная реализация#Удаление|удалим]] её, иначе найдём самую близкую по значению вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, переместим её на место удаляемой вершины и [[Дерево поиска, наивная реализация#Удаление|удалим]] вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. От удалённой вершины будем подниматься вверх к корню и пересчитывать баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; уменьшается на единицу, если из правого, то увеличивается на единицу. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
В результате указанных действий на удаление вершины и балансировку суммарно тратится, как и ранее, &amp;lt;tex&amp;gt; O(h) &amp;lt;/tex&amp;gt; операций. Таким образом, требуемое количество действий {{---}} &amp;lt;tex&amp;gt; O(\log{n}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Поиск вершины, минимум/максимум в дереве, etc. ===&lt;br /&gt;
Остальные операции не меняют структуры дерева, поэтому выполняются так же, как и в [[Дерево поиска, наивная реализация|наивной реализации]] дерева поиска.&lt;br /&gt;
===Слияние двух AVL-деревьев===&lt;br /&gt;
&lt;br /&gt;
Дано два дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, все ключи в &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;h(T_1) \leqslant h(T_2)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В дереве &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; удаляем самую правую вершину, назовём её &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;. Высота дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; может уменьшиться на единицу. В дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; идём от корня всегда в левое поддерево и, когда высота этого поддерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; будет равна высоте дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, делаем новое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; будет вершина &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, левым поддеревом будет дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, а правым дерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;. Теперь в дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; у вершины, в которой мы остановились при спуске, левым поддеревом делаем дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; до слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u3.jpg|340px]]&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; после слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u4.jpg|300px]]&lt;br /&gt;
&lt;br /&gt;
===Алгоритм разделения AVL-дерева на два===&lt;br /&gt;
====Алгоритм первый====&lt;br /&gt;
Пусть у нас есть дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Мы должны разбить его на два дерева &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; такие, что &amp;lt;tex&amp;gt;T_{1} \leqslant x&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;x &amp;lt; T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Предположим, что корень нашего дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, в таком случае все левое поддерево вместе с корнем после разделения отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда рекурсивно спускаемся в правое поддерево и там проверяем это условие (так как часть правого поддерева тоже может содержать ключи &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;). Если же корень оказался &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, то мы спускаемся той же рекурсией, но только в левое поддерево и ищем там.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. В таком случае этот корень со своим левым поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Поэтому мы делаем следующее: запоминаем ссылку на правое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, удаляем корень, запоминая его значение (не меняя конфигурацию дерева, то есть просто делаем ссылки на него NULL'ами). Таким образом, мы отделяем сбалансированное АВЛ-дерево (бывшее левое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Делаем новую вершину со значением бывшего корня правым листом самой правой вершины &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Обозначим полученное дерево за &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;. Теперь нам нужно объединить его с уже построенным ранее &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (оно может быть пустым, если мы первый раз нашли такое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Для этого мы ищем в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; самое правое поддерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; высоты, равной высоте &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (спускаясь от корня всегда в правые поддеревья). Делаем новое дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, сливая &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (очевидно, все ключи в &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, поэтому мы можем это сделать). Теперь в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; у отца вершины, в которой мы остановились при поиске дерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;, правым поддеревом делаем дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt; и запускаем балансировку. После нужно спуститься в правое поддерево бывшего дерева &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (по ссылке, которую мы ранее запомнили) и обработать его.&lt;br /&gt;
&lt;br /&gt;
Если мы пришли в поддерево &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, совершаем аналогичные действия: делаем NULL'ами ссылки на корень &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, запоминая ссылку на его левое поддерево. Делаем новую вершину со значением бывшего корня левым листом самой левой вершины &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt; и запускаем балансировку. Объединяем полученное АВЛ-дерево с уже построенным ранее &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; аналогичным первому случаю способом, только теперь мы ищем самое левое поддерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рассмотри пример (рис. 1). Цветом выделены поддеревья, которые после разделения должны отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;x = 76&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL.jpg|thumb|left|525px|Рис. 1. Разделение АВЛ-дерева на два.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Корень дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, поэтому он со всем выделенным поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. По описанному выше алгоритму отделяем это поддерево с корнем и делаем из них сбалансированное АВЛ-дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (рис. 2). Так как это первая ситуация, в которой корень рассматриваемого поддерева был &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; становится &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Далее по сохраненной ссылке спускаемся в правое поддерево. Его корень &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Следовательно, строим из него и его правого поддерева &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и спускаемся в левое поддерево. Снова корень &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Строим новое &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; и объединяем его с уже существующим &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (рис. 3).&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:АВВЛ2.jpg|thumb|left|525px|Рис. 2. Создание T'.]]&lt;br /&gt;
|}&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL3.jpg|thumb|left|1050px|Рис. 3. Объединение T' и T1.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Далее действуем по алгоритму и в итоге получаем (рис. 4):&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:End.jpg|thumb|left|525px|Рис. 4. АВЛ-деревья после разделения.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм имеет сложность &amp;lt;tex&amp;gt;O(\log^{2} n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
====Алгоритм второй====&lt;br /&gt;
Рассмотрим решение, которое имеет сложность &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Вернемся к примеру (рис. 1). Теперь рекурсивно спустимся вниз и оттуда будем строить деревья &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;, передавая наверх корректные АВЛ-деревья. То есть для рис. 1 первым в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; придет вершина &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt; с левым поддеревом (выделено светло-зеленым цветом), так как это корректное АВЛ-дерево, оно же и вернется из рекурсии. Далее мы попадем в вершину со значением &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt; и должны слить ее и ее левое поддерево (выделено светло-синим) с тем, что нам пришло. И сделать это нужно так, чтобы передать наверх корректное АВЛ-дерево. Будем действовать по такому алгоритму, пока не дойдем до вершины.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; с корнем &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Тогда сольем его с уже построенным на тот момент &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (&amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; пришло снизу, а значит по условию рекурсии это корректное АВЛ-дерево, &amp;lt;tex&amp;gt;S \leqslant T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(T_{1}) \leqslant h(S)&amp;lt;/tex&amp;gt;). Но так как обычная процедура слияния сливает два АВЛ-дерева, а &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не является корректным АВЛ-деревом, мы немного ее изменим. Пусть мы в дереве &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; нашли самое правое поддерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, высота которого равна высоте &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда сделаем новое дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, корнем которого будет вершина &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (без нее это дерево является сбалансированным), правым поддеревом {{---}} &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, левым {{---}} &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. И подвесим &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; на то место, где мы остановились при поиске &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. Запустим балансировку. В случае, когда корень поддерева, в которое мы пришли, &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, все аналогично.&lt;br /&gt;
&lt;br /&gt;
Разберем пример на рис. 1. Пусть мы рекурсивно спустились до узла &amp;lt;tex&amp;gt;77&amp;lt;/tex&amp;gt;. Ключ больше &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, поэтому эта вершина станет деревом &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и передастся наверх. Теперь мы поднялись в узел &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом станет деревом &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и мы снова поднимемся наверх в узел &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом снова должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, и так как теперь дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; уже не пустое, то их надо слить. После слияния по описанному выше алгоритму получим (рис. 5)&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex.jpg|thumb|left|525px|Рис. 5.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После мы поднимемся в вершину с ключом &amp;lt;tex&amp;gt;80&amp;lt;/tex&amp;gt;. Она с правым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; (рис. 6). &lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex2am.jpg|thumb|left|525px|Рис. 6.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
И на последней итерации мы поднимемся в корень дерева с ключом &amp;lt;tex&amp;gt;50&amp;lt;/tex&amp;gt;, он с левым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, после чего алгоритм завершится.&lt;br /&gt;
&lt;br /&gt;
Пусть поддеревьев с ключами &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt; оказалось больше, чем поддеревьев с ключами &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Докажем для них логарифмическую асимптотику. Дерево на последнем уровне имеет высоту &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; (она может быть не равна &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, если мы придём в &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;). Его мы передаем наверх и вставляем в поддерево высотой &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;H_{k} \leqslant H_{k-1}&amp;lt;/tex&amp;gt;, так как разница высот поддеревьев у любой вершины не больше &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, и мы при переходе от &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt; поднимаемся как минимум на одну вершину вверх. Слияние этих поддеревьев мы выполним за &amp;lt;tex&amp;gt;H_{k-1} - H_{k}&amp;lt;/tex&amp;gt;, получим в итоге дерево высоты не большей, чем &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. Его мы передадим наверх, поэтому в следующий раз слияние будет выполнено за &amp;lt;tex&amp;gt;H_{k-2} - H_{k - 1}&amp;lt;/tex&amp;gt; и так далее. Таким образом мы получим &amp;lt;tex&amp;gt;(H - H_{1}) + (H_{1} - H_{2}) + (H_{2} - H_{3}) + \cdots +  (H_{k - 1} - H_{k}) = H - H_{k} = O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итоговая асимптотика алгоритма {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [http://habrahabr.ru/post/150732/ Habrahabr {{---}} АВЛ-деревья]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47617</id>
		<title>АВЛ-дерево</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47617"/>
				<updated>2015-06-05T16:17:09Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: /* Высота дерева */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''АВЛ-дерево''' (''AVL-Tree'') {{---}} сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.&lt;br /&gt;
&lt;br /&gt;
АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г. М. Адельсона-Вельского и Е. М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962 году.&lt;br /&gt;
&lt;br /&gt;
== Высота дерева ==&lt;br /&gt;
{{Теорема &lt;br /&gt;
|statement=АВЛ-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключами имеет высоту &amp;lt;tex&amp;gt;h = O(\log N)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
||proof=&lt;br /&gt;
&lt;br /&gt;
Высоту поддерева с корнем &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; будем обозначать как &amp;lt;tex&amp;gt;h(x)&amp;lt;/tex&amp;gt;, высоту поддерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; {{---}} как &amp;lt;tex&amp;gt;h(T)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Пусть &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt;, где            &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фибоначчи.  &lt;br /&gt;
|proof=&lt;br /&gt;
Если &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;. Тогда, как легко видеть, &amp;lt;tex&amp;gt;m_{h+2} = m_{h+1} + m_h + 1&amp;lt;/tex&amp;gt;. Равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; докажем по индукции. &lt;br /&gt;
&lt;br /&gt;
База индукции &amp;lt;tex&amp;gt;m_1 = F_3 - 1&amp;lt;/tex&amp;gt; {{---}} верно, &amp;lt;tex&amp;gt;m_1 = 1, F_3 = 2&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Допустим &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} верно. &lt;br /&gt;
&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;m_{h+1} = m_h + m_{h-1} + 1 = F_{h+2} - 1 + F_{h+1} - 1 + 1 = F_{h+3} - 1&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Таким образом, равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} доказано.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\varphi = \dfrac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. То есть&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n \geqslant \varphi^{h}&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 h&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом,  получаем, что высота AVL-дерева из n вершин {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Балансировка ==&lt;br /&gt;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &amp;lt;tex&amp;gt;|h(L) - h(R)| = 2&amp;lt;/tex&amp;gt;, изменяет связи предок-потомок в поддереве данной вершины так, чтобы восстановилось свойство дерева &amp;lt;tex&amp;gt;|h(L) - h(R)| \leqslant 1&amp;lt;/tex&amp;gt;, иначе ничего не меняет.&lt;br /&gt;
Для балансировки будем хранить для каждой вершины разницу между высотой её левого и правого поддерева &amp;lt;tex&amp;gt;diff[i] = h(L) - h(R)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для балансировки вершины используются один из 4 типов вращений:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
 !Тип вращения&lt;br /&gt;
 !Иллюстрация&lt;br /&gt;
 !Когда используется&lt;br /&gt;
 !Расстановка балансов&lt;br /&gt;
 !Псевдокод&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое левое вращение''' (''Small left rotation'')&lt;br /&gt;
 |  [[Файл:avl_u1.jpg|2000x200px]]  &lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |node* rotateleft(node* a) {&lt;br /&gt;
    node* b = a-&amp;gt;right;&lt;br /&gt;
    a-&amp;gt;right = b-&amp;gt;left;&lt;br /&gt;
    b-&amp;gt;left = a;&lt;br /&gt;
    fixheight(a);&lt;br /&gt;
    fixheight(b);&lt;br /&gt;
    return b;&lt;br /&gt;
}&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение''' (Big left rotation'')&lt;br /&gt;
 | [[Файл:avl_u2.jpg|2000x200px]]&lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; , &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |node* bigrotateleft(node* a) {&lt;br /&gt;
    rotateright(a-&amp;gt;right);&lt;br /&gt;
    rotateleft(a);&lt;br /&gt;
    return a;&lt;br /&gt;
}&lt;br /&gt;
 |}&lt;br /&gt;
Малое правое и большое правое вращение определяются симметрично малому левому и большому левому вращению.&lt;br /&gt;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 1 и не может увеличиться.&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;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Будем спускаться по дереву, как при поиске ключа &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Если мы стоим в вершине &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и нам надо идти в поддерево, которого нет, то делаем ключ &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; листом, а вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; его корнем. Дальше поднимаемся вверх по пути поиска и пересчитываем баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
Так как в процессе добавления вершины мы рассматриваем не более, чем &amp;lt;tex&amp;gt; O(h) &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;
Если вершина - лист, то [[Дерево поиска, наивная реализация#Удаление|удалим]] её, иначе найдём самую близкую по значению вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, переместим её на место удаляемой вершины и [[Дерево поиска, наивная реализация#Удаление|удалим]] вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. От удалённой вершины будем подниматься вверх к корню и пересчитывать баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; уменьшается на единицу, если из правого, то увеличивается на единицу. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
В результате указанных действий на удаление вершины и балансировку суммарно тратится, как и ранее, &amp;lt;tex&amp;gt; O(h) &amp;lt;/tex&amp;gt; операций. Таким образом, требуемое количество действий {{---}} &amp;lt;tex&amp;gt; O(\log{n}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Поиск вершины, минимум/максимум в дереве, etc. ===&lt;br /&gt;
Остальные операции не меняют структуры дерева, поэтому выполняются так же, как и в [[Дерево поиска, наивная реализация|наивной реализации]] дерева поиска.&lt;br /&gt;
===Слияние двух AVL-деревьев===&lt;br /&gt;
&lt;br /&gt;
Дано два дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, все ключи в &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;h(T_1) \leqslant h(T_2)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В дереве &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; удаляем самую правую вершину, назовём её &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;. Высота дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; может уменьшиться на единицу. В дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; идём от корня всегда в левое поддерево и, когда высота этого поддерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; будет равна высоте дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, делаем новое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; будет вершина &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, левым поддеревом будет дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, а правым дерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;. Теперь в дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; у вершины, в которой мы остановились при спуске, левым поддеревом делаем дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; до слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u3.jpg|340px]]&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; после слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u4.jpg|300px]]&lt;br /&gt;
&lt;br /&gt;
===Алгоритм разделения AVL-дерева на два===&lt;br /&gt;
====Алгоритм первый====&lt;br /&gt;
Пусть у нас есть дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Мы должны разбить его на два дерева &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; такие, что &amp;lt;tex&amp;gt;T_{1} \leqslant x&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;x &amp;lt; T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Предположим, что корень нашего дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, в таком случае все левое поддерево вместе с корнем после разделения отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда рекурсивно спускаемся в правое поддерево и там проверяем это условие (так как часть правого поддерева тоже может содержать ключи &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;). Если же корень оказался &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, то мы спускаемся той же рекурсией, но только в левое поддерево и ищем там.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. В таком случае этот корень со своим левым поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Поэтому мы делаем следующее: запоминаем ссылку на правое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, удаляем корень, запоминая его значение (не меняя конфигурацию дерева, то есть просто делаем ссылки на него NULL'ами). Таким образом, мы отделяем сбалансированное АВЛ-дерево (бывшее левое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Делаем новую вершину со значением бывшего корня правым листом самой правой вершины &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Обозначим полученное дерево за &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;. Теперь нам нужно объединить его с уже построенным ранее &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (оно может быть пустым, если мы первый раз нашли такое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Для этого мы ищем в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; самое правое поддерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; высоты, равной высоте &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (спускаясь от корня всегда в правые поддеревья). Делаем новое дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, сливая &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (очевидно, все ключи в &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, поэтому мы можем это сделать). Теперь в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; у отца вершины, в которой мы остановились при поиске дерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;, правым поддеревом делаем дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt; и запускаем балансировку. После нужно спуститься в правое поддерево бывшего дерева &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (по ссылке, которую мы ранее запомнили) и обработать его.&lt;br /&gt;
&lt;br /&gt;
Если мы пришли в поддерево &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, совершаем аналогичные действия: делаем NULL'ами ссылки на корень &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, запоминая ссылку на его левое поддерево. Делаем новую вершину со значением бывшего корня левым листом самой левой вершины &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt; и запускаем балансировку. Объединяем полученное АВЛ-дерево с уже построенным ранее &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; аналогичным первому случаю способом, только теперь мы ищем самое левое поддерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рассмотри пример (рис. 1). Цветом выделены поддеревья, которые после разделения должны отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;x = 76&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL.jpg|thumb|left|525px|Рис. 1. Разделение АВЛ-дерева на два.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Корень дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, поэтому он со всем выделенным поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. По описанному выше алгоритму отделяем это поддерево с корнем и делаем из них сбалансированное АВЛ-дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (рис. 2). Так как это первая ситуация, в которой корень рассматриваемого поддерева был &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; становится &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Далее по сохраненной ссылке спускаемся в правое поддерево. Его корень &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Следовательно, строим из него и его правого поддерева &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и спускаемся в левое поддерево. Снова корень &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Строим новое &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; и объединяем его с уже существующим &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (рис. 3).&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:АВВЛ2.jpg|thumb|left|525px|Рис. 2. Создание T'.]]&lt;br /&gt;
|}&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL3.jpg|thumb|left|1050px|Рис. 3. Объединение T' и T1.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Далее действуем по алгоритму и в итоге получаем (рис. 4):&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:End.jpg|thumb|left|525px|Рис. 4. АВЛ-деревья после разделения.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм имеет сложность &amp;lt;tex&amp;gt;O(\log^{2} n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
====Алгоритм второй====&lt;br /&gt;
Рассмотрим решение, которое имеет сложность &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Вернемся к примеру (рис. 1). Теперь рекурсивно спустимся вниз и оттуда будем строить деревья &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;, передавая наверх корректные АВЛ-деревья. То есть для рис. 1 первым в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; придет вершина &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt; с левым поддеревом (выделено светло-зеленым цветом), так как это корректное АВЛ-дерево, оно же и вернется из рекурсии. Далее мы попадем в вершину со значением &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt; и должны слить ее и ее левое поддерево (выделено светло-синим) с тем, что нам пришло. И сделать это нужно так, чтобы передать наверх корректное АВЛ-дерево. Будем действовать по такому алгоритму, пока не дойдем до вершины.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; с корнем &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Тогда сольем его с уже построенным на тот момент &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (&amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; пришло снизу, а значит по условию рекурсии это корректное АВЛ-дерево, &amp;lt;tex&amp;gt;S \leqslant T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(T_{1}) \leqslant h(S)&amp;lt;/tex&amp;gt;). Но так как обычная процедура слияния сливает два АВЛ-дерева, а &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не является корректным АВЛ-деревом, мы немного ее изменим. Пусть мы в дереве &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; нашли самое правое поддерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, высота которого равна высоте &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда сделаем новое дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, корнем которого будет вершина &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (без нее это дерево является сбалансированным), правым поддеревом {{---}} &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, левым {{---}} &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. И подвесим &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; на то место, где мы остановились при поиске &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. Запустим балансировку. В случае, когда корень поддерева, в которое мы пришли, &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, все аналогично.&lt;br /&gt;
&lt;br /&gt;
Разберем пример на рис. 1. Пусть мы рекурсивно спустились до узла &amp;lt;tex&amp;gt;77&amp;lt;/tex&amp;gt;. Ключ больше &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, поэтому эта вершина станет деревом &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и передастся наверх. Теперь мы поднялись в узел &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом станет деревом &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и мы снова поднимемся наверх в узел &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом снова должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, и так как теперь дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; уже не пустое, то их надо слить. После слияния по описанному выше алгоритму получим (рис. 5)&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex.jpg|thumb|left|525px|Рис. 5.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После мы поднимемся в вершину с ключом &amp;lt;tex&amp;gt;80&amp;lt;/tex&amp;gt;. Она с правым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; (рис. 6). &lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex2am.jpg|thumb|left|525px|Рис. 6.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
И на последней итерации мы поднимемся в корень дерева с ключом &amp;lt;tex&amp;gt;50&amp;lt;/tex&amp;gt;, он с левым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, после чего алгоритм завершится.&lt;br /&gt;
&lt;br /&gt;
Пусть поддеревьев с ключами &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt; оказалось больше, чем поддеревьев с ключами &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Докажем для них логарифмическую асимптотику. Дерево на последнем уровне имеет высоту &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; (она может быть не равна &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, если мы придём в &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;). Его мы передаем наверх и вставляем в поддерево высотой &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;H_{k} \leqslant H_{k-1}&amp;lt;/tex&amp;gt;, так как разница высот поддеревьев у любой вершины не больше &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, и мы при переходе от &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt; поднимаемся как минимум на одну вершину вверх. Слияние этих поддеревьев мы выполним за &amp;lt;tex&amp;gt;H_{k-1} - H_{k}&amp;lt;/tex&amp;gt;, получим в итоге дерево высоты не большей, чем &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. Его мы передадим наверх, поэтому в следующий раз слияние будет выполнено за &amp;lt;tex&amp;gt;H_{k-2} - H_{k - 1}&amp;lt;/tex&amp;gt; и так далее. Таким образом мы получим &amp;lt;tex&amp;gt;(H - H_{1}) + (H_{1} - H_{2}) + (H_{2} - H_{3}) + \cdots +  (H_{k - 1} - H_{k}) = H - H_{k} = O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итоговая асимптотика алгоритма {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [http://habrahabr.ru/post/150732/ Habrahabr {{---}} АВЛ-деревья]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47404</id>
		<title>АВЛ-дерево</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47404"/>
				<updated>2015-06-04T08:31:40Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''АВЛ-дерево''' (''AVL-Tree'') {{---}} сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.&lt;br /&gt;
&lt;br /&gt;
АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г. М. Адельсона-Вельского и Е. М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962 году.&lt;br /&gt;
&lt;br /&gt;
== Высота дерева ==&lt;br /&gt;
{{Теорема &lt;br /&gt;
|statement=АВЛ-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключами имеет высоту &amp;lt;tex&amp;gt;h = O(\log N)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
||proof=&lt;br /&gt;
&lt;br /&gt;
Высоту поддерева с корнем &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; будем обозначать как &amp;lt;tex&amp;gt;h(x)&amp;lt;/tex&amp;gt;, высоту поддерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; {{---}} как &amp;lt;tex&amp;gt;h(T)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Пусть &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt;, где            &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фибоначчи.  &lt;br /&gt;
|proof=&lt;br /&gt;
Если &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;. Тогда, как легко видеть, &amp;lt;tex&amp;gt;m_{h+2} = m_{h+1} + m_h + 1&amp;lt;/tex&amp;gt;. Равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; докажем по индукции. &lt;br /&gt;
&lt;br /&gt;
База индукции &amp;lt;tex&amp;gt;m_1 = F_3 - 1&amp;lt;/tex&amp;gt; {{---}} верно, &amp;lt;tex&amp;gt;m_1 = 1, F_3 = 2&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Допустим &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} верно. &lt;br /&gt;
&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;m_{h+1} = m_h + m_{h-1} + 1 = F_{h+2} - 1 + F_{h+1} - 1 + 1 = F_{h+3} - 1&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Таким образом, равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} доказано.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\varphi = \frac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. То есть&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n \geqslant \varphi^{h}&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 h&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом,  получаем, что высота AVL-дерева из n вершин {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Балансировка ==&lt;br /&gt;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &amp;lt;tex&amp;gt;|h(L) - h(R)| = 2&amp;lt;/tex&amp;gt;, изменяет связи предок-потомок в поддереве данной вершины так, чтобы восстановилось свойство дерева &amp;lt;tex&amp;gt;|h(L) - h(R)| \leqslant 1&amp;lt;/tex&amp;gt;, иначе ничего не меняет.&lt;br /&gt;
Для балансировки будем хранить для каждой вершины разницу между высотой её левого и правого поддерева &amp;lt;tex&amp;gt;diff[i] = h(L) - h(R)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для балансировки вершины используются один из 4 типов вращений:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
 !Тип вращения&lt;br /&gt;
 !Иллюстрация&lt;br /&gt;
 !Когда используется&lt;br /&gt;
 !Расстановка балансов&lt;br /&gt;
 !Псевдокод&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое левое вращение''' (''Small left rotation'')&lt;br /&gt;
 |  [[Файл:avl_u1.jpg|2000x200px]]  &lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |node* rotateleft(node* a) {&lt;br /&gt;
    node* b = a-&amp;gt;right;&lt;br /&gt;
    a-&amp;gt;right = b-&amp;gt;left;&lt;br /&gt;
    b-&amp;gt;left = a;&lt;br /&gt;
    fixheight(a);&lt;br /&gt;
    fixheight(b);&lt;br /&gt;
    return b;&lt;br /&gt;
}&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение''' (Big left rotation'')&lt;br /&gt;
 | [[Файл:avl_u2.jpg|2000x200px]]&lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; , &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |node* bigrotateleft(node* a) {&lt;br /&gt;
    rotateright(a-&amp;gt;right);&lt;br /&gt;
    rotateleft(a);&lt;br /&gt;
    return a;&lt;br /&gt;
}&lt;br /&gt;
 |}&lt;br /&gt;
Малое правое и большое правое вращение определяются симметрично малому левому и большому левому вращению.&lt;br /&gt;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 1 и не может увеличиться.&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;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Будем спускаться по дереву, как при поиске ключа &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Если мы стоим в вершине &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и нам надо идти в поддерево, которого нет, то делаем ключ &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; листом, а вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; его корнем. Дальше поднимаемся вверх по пути поиска и пересчитываем баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
Так как в процессе добавления вершины мы рассматриваем не более, чем &amp;lt;tex&amp;gt; O(h) &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;
Если вершина - лист, то [[Дерево поиска, наивная реализация#Удаление|удалим]] её, иначе найдём самую близкую по значению вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, переместим её на место удаляемой вершины и [[Дерево поиска, наивная реализация#Удаление|удалим]] вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. От удалённой вершины будем подниматься вверх к корню и пересчитывать баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; уменьшается на единицу, если из правого, то увеличивается на единицу. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
В результате указанных действий на удаление вершины и балансировку суммарно тратится, как и ранее, &amp;lt;tex&amp;gt; O(h) &amp;lt;/tex&amp;gt; операций. Таким образом, требуемое количество действий {{---}} &amp;lt;tex&amp;gt; O(\log{n}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Поиск вершины, минимум/максимум в дереве, etc. ===&lt;br /&gt;
Остальные операции не меняют структуры дерева, поэтому выполняются так же, как и в [[Дерево поиска, наивная реализация|наивной реализации]] дерева поиска.&lt;br /&gt;
===Слияние двух AVL-деревьев===&lt;br /&gt;
&lt;br /&gt;
Дано два дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, все ключи в &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;h(T_1) \leqslant h(T_2)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В дереве &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; удаляем самую правую вершину, назовём её &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;. Высота дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; может уменьшиться на единицу. В дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; идём от корня всегда в левое поддерево и, когда высота этого поддерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; будет равна высоте дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, делаем новое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; будет вершина &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, левым поддеревом будет дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, а правым дерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;. Теперь в дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; у вершины, в которой мы остановились при спуске, левым поддеревом делаем дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; до слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u3.jpg|340px]]&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; после слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u4.jpg|300px]]&lt;br /&gt;
&lt;br /&gt;
===Алгоритм разделения AVL-дерева на два===&lt;br /&gt;
====Алгоритм первый====&lt;br /&gt;
Пусть у нас есть дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Мы должны разбить его на два дерева &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; такие, что &amp;lt;tex&amp;gt;T_{1} \leqslant x&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;x &amp;lt; T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Предположим, что корень нашего дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, в таком случае все левое поддерево вместе с корнем после разделения отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда рекурсивно спускаемся в правое поддерево и там проверяем это условие (так как часть правого поддерева тоже может содержать ключи &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;). Если же корень оказался &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, то мы спускаемся той же рекурсией, но только в левое поддерево и ищем там.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. В таком случае этот корень со своим левым поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Поэтому мы делаем следующее: запоминаем ссылку на правое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, удаляем корень, запоминая его значение (не меняя конфигурацию дерева, то есть просто делаем ссылки на него NULL'ами). Таким образом, мы отделяем сбалансированное АВЛ-дерево (бывшее левое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Делаем новую вершину со значением бывшего корня правым листом самой правой вершины &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Обозначим полученное дерево за &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;. Теперь нам нужно объединить его с уже построенным ранее &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (оно может быть пустым, если мы первый раз нашли такое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Для этого мы ищем в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; самое правое поддерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; высоты, равной высоте &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (спускаясь от корня всегда в правые поддеревья). Делаем новое дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, сливая &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (очевидно, все ключи в &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, поэтому мы можем это сделать). Теперь в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; у отца вершины, в которой мы остановились при поиске дерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;, правым поддеревом делаем дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt; и запускаем балансировку. После нужно спуститься в правое поддерево бывшего дерева &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (по ссылке, которую мы ранее запомнили) и обработать его.&lt;br /&gt;
&lt;br /&gt;
Если мы пришли в поддерево &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, совершаем аналогичные действия: делаем NULL'ами ссылки на корень &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, запоминая ссылку на его левое поддерево. Делаем новую вершину со значением бывшего корня левым листом самой левой вершины &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt; и запускаем балансировку. Объединяем полученное АВЛ-дерево с уже построенным ранее &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; аналогичным первому случаю способом, только теперь мы ищем самое левое поддерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рассмотри пример (рис. 1). Цветом выделены поддеревья, которые после разделения должны отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;x = 76&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL.jpg|thumb|left|525px|Рис. 1. Разделение АВЛ-дерева на два.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Корень дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, поэтому он со всем выделенным поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. По описанному выше алгоритму отделяем это поддерево с корнем и делаем из них сбалансированное АВЛ-дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (рис. 2). Так как это первая ситуация, в которой корень рассматриваемого поддерева был &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; становится &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Далее по сохраненной ссылке спускаемся в правое поддерево. Его корень &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Следовательно, строим из него и его правого поддерева &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и спускаемся в левое поддерево. Снова корень &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Строим новое &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; и объединяем его с уже существующим &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (рис. 3).&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:АВВЛ2.jpg|thumb|left|525px|Рис. 2. Создание T'.]]&lt;br /&gt;
|}&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL3.jpg|thumb|left|1050px|Рис. 3. Объединение T' и T1.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Далее действуем по алгоритму и в итоге получаем (рис. 4):&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:End.jpg|thumb|left|525px|Рис. 4. АВЛ-деревья после разделения.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм имеет сложность &amp;lt;tex&amp;gt;O(\log^{2} n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
====Алгоритм второй====&lt;br /&gt;
Рассмотрим решение, которое имеет сложность &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Вернемся к примеру (рис. 1). Теперь рекурсивно спустимся вниз и оттуда будем строить деревья &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;, передавая наверх корректные АВЛ-деревья. То есть для рис. 1 первым в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; придет вершина &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt; с левым поддеревом (выделено светло-зеленым цветом), так как это корректное АВЛ-дерево, оно же и вернется из рекурсии. Далее мы попадем в вершину со значением &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt; и должны слить ее и ее левое поддерево (выделено светло-синим) с тем, что нам пришло. И сделать это нужно так, чтобы передать наверх корректное АВЛ-дерево. Будем действовать по такому алгоритму, пока не дойдем до вершины.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; с корнем &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Тогда сольем его с уже построенным на тот момент &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (&amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; пришло снизу, а значит по условию рекурсии это корректное АВЛ-дерево, &amp;lt;tex&amp;gt;S \leqslant T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(T_{1}) \leqslant h(S)&amp;lt;/tex&amp;gt;). Но так как обычная процедура слияния сливает два АВЛ-дерева, а &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не является корректным АВЛ-деревом, мы немного ее изменим. Пусть мы в дереве &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; нашли самое правое поддерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, высота которого равна высоте &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда сделаем новое дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, корнем которого будет вершина &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (без нее это дерево является сбалансированным), правым поддеревом {{---}} &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, левым {{---}} &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. И подвесим &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; на то место, где мы остановились при поиске &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. Запустим балансировку. В случае, когда корень поддерева, в которое мы пришли, &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, все аналогично.&lt;br /&gt;
&lt;br /&gt;
Разберем пример на рис. 1. Пусть мы рекурсивно спустились до узла &amp;lt;tex&amp;gt;77&amp;lt;/tex&amp;gt;. Ключ больше &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, поэтому эта вершина станет деревом &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и передастся наверх. Теперь мы поднялись в узел &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом станет деревом &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и мы снова поднимемся наверх в узел &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом снова должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, и так как теперь дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; уже не пустое, то их надо слить. После слияния по описанному выше алгоритму получим (рис. 5)&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex.jpg|thumb|left|525px|Рис. 5.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После мы поднимемся в вершину с ключом &amp;lt;tex&amp;gt;80&amp;lt;/tex&amp;gt;. Она с правым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; (рис. 6). &lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex2am.jpg|thumb|left|525px|Рис. 6.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
И на последней итерации мы поднимемся в корень дерева с ключом &amp;lt;tex&amp;gt;50&amp;lt;/tex&amp;gt;, он с левым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, после чего алгоритм завершится.&lt;br /&gt;
&lt;br /&gt;
Пусть поддеревьев с ключами &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt; оказалось больше, чем поддеревьев с ключами &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Докажем для них логарифмическую асимптотику. Дерево на последнем уровне имеет высоту &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; (она может быть не равна &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, если мы придём в &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;). Его мы передаем наверх и вставляем в поддерево высотой &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;H_{k} \leqslant H_{k-1}&amp;lt;/tex&amp;gt;, так как разница высот поддеревьев у любой вершины не больше &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, и мы при переходе от &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt; поднимаемся как минимум на одну вершину вверх. Слияние этих поддеревьев мы выполним за &amp;lt;tex&amp;gt;H_{k-1} - H_{k}&amp;lt;/tex&amp;gt;, получим в итоге дерево высоты не большей, чем &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. Его мы передадим наверх, поэтому в следующий раз слияние будет выполнено за &amp;lt;tex&amp;gt;H_{k-2} - H_{k - 1}&amp;lt;/tex&amp;gt; и так далее. Таким образом мы получим &amp;lt;tex&amp;gt;(H - H_{1}) + (H_{1} - H_{2}) + (H_{2} - H_{3}) + \cdots +  (H_{k - 1} - H_{k}) = H - H_{k} = O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итоговая асимптотика алгоритма {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [http://habrahabr.ru/post/150732/ Habrahabr {{---}} АВЛ-деревья]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47403</id>
		<title>АВЛ-дерево</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=47403"/>
				<updated>2015-06-04T08:30:57Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: /* Балансировка */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''АВЛ-дерево''' (''AVL-Tree'') {{---}} сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.&lt;br /&gt;
&lt;br /&gt;
АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г. М. Адельсона-Вельского и Е. М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962 году.&lt;br /&gt;
&lt;br /&gt;
== Высота дерева ==&lt;br /&gt;
{{Теорема &lt;br /&gt;
|statement=АВЛ-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключами имеет высоту &amp;lt;tex&amp;gt;h = O(\log N)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
||proof=&lt;br /&gt;
&lt;br /&gt;
Высоту поддерева с корнем &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; будем обозначать как &amp;lt;tex&amp;gt;h(x)&amp;lt;/tex&amp;gt;, высоту поддерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; {{---}} как &amp;lt;tex&amp;gt;h(T)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Пусть &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt;, где            &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фибоначчи.  &lt;br /&gt;
|proof=&lt;br /&gt;
Если &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;. Тогда, как легко видеть, &amp;lt;tex&amp;gt;m_{h+2} = m_{h+1} + m_h + 1&amp;lt;/tex&amp;gt;. Равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; докажем по индукции. &lt;br /&gt;
&lt;br /&gt;
База индукции &amp;lt;tex&amp;gt;m_1 = F_3 - 1&amp;lt;/tex&amp;gt; {{---}} верно, &amp;lt;tex&amp;gt;m_1 = 1, F_3 = 2&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Допустим &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} верно. &lt;br /&gt;
&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;m_{h+1} = m_h + m_{h-1} + 1 = F_{h+2} - 1 + F_{h+1} - 1 + 1 = F_{h+3} - 1&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Таким образом, равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} доказано.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\varphi = \frac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. То есть&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n \geqslant \varphi^{h}&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 h&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом,  получаем, что высота AVL-дерева из n вершин {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Балансировка ==&lt;br /&gt;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &amp;lt;tex&amp;gt;|h(L) - h(R)| = 2&amp;lt;/tex&amp;gt;, изменяет связи предок-потомок в поддереве данной вершины так, чтобы восстановилось свойство дерева &amp;lt;tex&amp;gt;|h(L) - h(R)| \leqslant 1&amp;lt;/tex&amp;gt;, иначе ничего не меняет.&lt;br /&gt;
Для балансировки будем хранить для каждой вершины разницу между высотой её левого и правого поддерева &amp;lt;tex&amp;gt;diff[i] = h(L) - h(R)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для балансировки вершины используются один из 4 типов вращений:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
 !Тип вращения&lt;br /&gt;
 !Иллюстрация&lt;br /&gt;
 !Когда используется&lt;br /&gt;
 !Расстановка балансов&lt;br /&gt;
 !Псевдокод&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое левое вращение''' (''Small left rotation'')&lt;br /&gt;
 |  [[Файл:avl_u1.jpg|2000x200px]]  &lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |node* rotateleft(node* a) {&lt;br /&gt;
    node* b = a-&amp;gt;right;&lt;br /&gt;
    a-&amp;gt;right = b-&amp;gt;left;&lt;br /&gt;
    b-&amp;gt;left = a;&lt;br /&gt;
    fixheight(a);&lt;br /&gt;
    fixheight(b);&lt;br /&gt;
    return b;&lt;br /&gt;
}&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение''' (Big left rotation'')&lt;br /&gt;
 | [[Файл:avl_u2.jpg|2000x200px]]&lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; , &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
 |node* bigrotateleft(node* a) {&lt;br /&gt;
    rotateright(a-&amp;gt;right);&lt;br /&gt;
    rotateleft(a);&lt;br /&gt;
    return a;&lt;br /&gt;
}&lt;br /&gt;
 |}&lt;br /&gt;
Малое правое и большое правое вращение определяются симметрично малому левому и большому левому вращению.&lt;br /&gt;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 1 и не может увеличиться.&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;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Будем спускаться по дереву, как при поиске ключа &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Если мы стоим в вершине &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и нам надо идти в поддерево, которого нет, то делаем ключ &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; листом, а вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; его корнем. Дальше поднимаемся вверх по пути поиска и пересчитываем баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
Так как в процессе добавления вершины мы рассматриваем не более, чем &amp;lt;tex&amp;gt; O(h) &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;
Если вершина - лист, то [[Дерево поиска, наивная реализация#Удаление|удалим]] её, иначе найдём самую близкую по значению вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, переместим её на место удаляемой вершины и [[Дерево поиска, наивная реализация#Удаление|удалим]] вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. От удалённой вершины будем подниматься вверх к корню и пересчитывать баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; уменьшается на единицу, если из правого, то увеличивается на единицу. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
В результате указанных действий на удаление вершины и балансировку суммарно тратится, как и ранее, &amp;lt;tex&amp;gt; O(h) &amp;lt;/tex&amp;gt; операций. Таким образом, требуемое количество действий {{---}} &amp;lt;tex&amp;gt; O(\log{n}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Поиск вершины, минимум/максимум в дереве, etc. ===&lt;br /&gt;
Остальные операции не меняют структуры дерева, поэтому выполняются так же, как и в [[Дерево поиска, наивная реализация|наивной реализации]] дерева поиска.&lt;br /&gt;
===Слияние двух AVL-деревьев===&lt;br /&gt;
&lt;br /&gt;
Дано два дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, все ключи в &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;h(T_1) \leqslant h(T_2)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В дереве &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; удаляем самую правую вершину, назовём её &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;. Высота дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; может уменьшиться на единицу. В дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; идём от корня всегда в левое поддерево и, когда высота этого поддерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; будет равна высоте дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, делаем новое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; будет вершина &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, левым поддеревом будет дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, а правым дерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;. Теперь в дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; у вершины, в которой мы остановились при спуске, левым поддеревом делаем дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; до слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u3.jpg|340px]]&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; после слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u4.jpg|300px]]&lt;br /&gt;
&lt;br /&gt;
===Алгоритм разделения AVL-дерева на два===&lt;br /&gt;
====Алгоритм первый====&lt;br /&gt;
Пусть у нас есть дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Мы должны разбить его на два дерева &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; такие, что &amp;lt;tex&amp;gt;T_{1} \leqslant x&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;x &amp;lt; T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Предположим, что корень нашего дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, в таком случае все левое поддерево вместе с корнем после разделения отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда рекурсивно спускаемся в правое поддерево и там проверяем это условие (так как часть правого поддерева тоже может содержать ключи &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;). Если же корень оказался &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, то мы спускаемся той же рекурсией, но только в левое поддерево и ищем там.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. В таком случае этот корень со своим левым поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Поэтому мы делаем следующее: запоминаем ссылку на правое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, удаляем корень, запоминая его значение (не меняя конфигурацию дерева, то есть просто делаем ссылки на него NULL'ами). Таким образом, мы отделяем сбалансированное АВЛ-дерево (бывшее левое поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Делаем новую вершину со значением бывшего корня правым листом самой правой вершины &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Обозначим полученное дерево за &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;. Теперь нам нужно объединить его с уже построенным ранее &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (оно может быть пустым, если мы первый раз нашли такое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;). Для этого мы ищем в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; самое правое поддерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; высоты, равной высоте &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (спускаясь от корня всегда в правые поддеревья). Делаем новое дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, сливая &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (очевидно, все ключи в &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, поэтому мы можем это сделать). Теперь в дереве &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; у отца вершины, в которой мы остановились при поиске дерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;, правым поддеревом делаем дерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt; и запускаем балансировку. После нужно спуститься в правое поддерево бывшего дерева &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (по ссылке, которую мы ранее запомнили) и обработать его.&lt;br /&gt;
&lt;br /&gt;
Если мы пришли в поддерево &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, корень которого &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, совершаем аналогичные действия: делаем NULL'ами ссылки на корень &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, запоминая ссылку на его левое поддерево. Делаем новую вершину со значением бывшего корня левым листом самой левой вершины &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt; и запускаем балансировку. Объединяем полученное АВЛ-дерево с уже построенным ранее &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; аналогичным первому случаю способом, только теперь мы ищем самое левое поддерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рассмотри пример (рис. 1). Цветом выделены поддеревья, которые после разделения должны отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;x = 76&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL.jpg|thumb|left|525px|Рис. 1. Разделение АВЛ-дерева на два.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Корень дерева &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, поэтому он со всем выделенным поддеревом должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. По описанному выше алгоритму отделяем это поддерево с корнем и делаем из них сбалансированное АВЛ-дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; (рис. 2). Так как это первая ситуация, в которой корень рассматриваемого поддерева был &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; становится &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Далее по сохраненной ссылке спускаемся в правое поддерево. Его корень &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Следовательно, строим из него и его правого поддерева &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и спускаемся в левое поддерево. Снова корень &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Строим новое &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; и объединяем его с уже существующим &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (рис. 3).&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:АВВЛ2.jpg|thumb|left|525px|Рис. 2. Создание T'.]]&lt;br /&gt;
|}&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:AVL3.jpg|thumb|left|1050px|Рис. 3. Объединение T' и T1.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Далее действуем по алгоритму и в итоге получаем (рис. 4):&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:End.jpg|thumb|left|525px|Рис. 4. АВЛ-деревья после разделения.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм имеет сложность &amp;lt;tex&amp;gt;O(\log^{2} n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
====Алгоритм второй====&lt;br /&gt;
Рассмотрим решение, которое имеет сложность &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Вернемся к примеру (рис. 1). Теперь рекурсивно спустимся вниз и оттуда будем строить деревья &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt;, передавая наверх корректные АВЛ-деревья. То есть для рис. 1 первым в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; придет вершина &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt; с левым поддеревом (выделено светло-зеленым цветом), так как это корректное АВЛ-дерево, оно же и вернется из рекурсии. Далее мы попадем в вершину со значением &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt; и должны слить ее и ее левое поддерево (выделено светло-синим) с тем, что нам пришло. И сделать это нужно так, чтобы передать наверх корректное АВЛ-дерево. Будем действовать по такому алгоритму, пока не дойдем до вершины.&lt;br /&gt;
&lt;br /&gt;
Пусть мы пришли в поддерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; с корнем &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt;. Тогда сольем его с уже построенным на тот момент &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; (&amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; пришло снизу, а значит по условию рекурсии это корректное АВЛ-дерево, &amp;lt;tex&amp;gt;S \leqslant T_{1}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(T_{1}) \leqslant h(S)&amp;lt;/tex&amp;gt;). Но так как обычная процедура слияния сливает два АВЛ-дерева, а &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не является корректным АВЛ-деревом, мы немного ее изменим. Пусть мы в дереве &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; нашли самое правое поддерево &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;, высота которого равна высоте &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;. Тогда сделаем новое дерево &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt;, корнем которого будет вершина &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; (без нее это дерево является сбалансированным), правым поддеревом {{---}} &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, левым {{---}} &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. И подвесим &amp;lt;tex&amp;gt;T'&amp;lt;/tex&amp;gt; на то место, где мы остановились при поиске &amp;lt;tex&amp;gt;K&amp;lt;/tex&amp;gt;. Запустим балансировку. В случае, когда корень поддерева, в которое мы пришли, &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;, все аналогично.&lt;br /&gt;
&lt;br /&gt;
Разберем пример на рис. 1. Пусть мы рекурсивно спустились до узла &amp;lt;tex&amp;gt;77&amp;lt;/tex&amp;gt;. Ключ больше &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, поэтому эта вершина станет деревом &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; и передастся наверх. Теперь мы поднялись в узел &amp;lt;tex&amp;gt;75&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом станет деревом &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; и мы снова поднимемся наверх в узел &amp;lt;tex&amp;gt;70&amp;lt;/tex&amp;gt;. Он со своим левым поддеревом снова должен отойти в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, и так как теперь дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt; уже не пустое, то их надо слить. После слияния по описанному выше алгоритму получим (рис. 5)&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex.jpg|thumb|left|525px|Рис. 5.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
После мы поднимемся в вершину с ключом &amp;lt;tex&amp;gt;80&amp;lt;/tex&amp;gt;. Она с правым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{2}&amp;lt;/tex&amp;gt; (рис. 6). &lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot;&lt;br /&gt;
| || [[Файл:Ex2am.jpg|thumb|left|525px|Рис. 6.]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
И на последней итерации мы поднимемся в корень дерева с ключом &amp;lt;tex&amp;gt;50&amp;lt;/tex&amp;gt;, он с левым поддеревом отойдет в дерево &amp;lt;tex&amp;gt;T_{1}&amp;lt;/tex&amp;gt;, после чего алгоритм завершится.&lt;br /&gt;
&lt;br /&gt;
Пусть поддеревьев с ключами &amp;lt;tex&amp;gt;\leqslant x&amp;lt;/tex&amp;gt; оказалось больше, чем поддеревьев с ключами &amp;lt;tex&amp;gt;&amp;gt; x&amp;lt;/tex&amp;gt;. Докажем для них логарифмическую асимптотику. Дерево на последнем уровне имеет высоту &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; (она может быть не равна &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, если мы придём в &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;). Его мы передаем наверх и вставляем в поддерево высотой &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;H_{k} \leqslant H_{k-1}&amp;lt;/tex&amp;gt;, так как разница высот поддеревьев у любой вершины не больше &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, и мы при переходе от &amp;lt;tex&amp;gt;H_{k}&amp;lt;/tex&amp;gt; к &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt; поднимаемся как минимум на одну вершину вверх. Слияние этих поддеревьев мы выполним за &amp;lt;tex&amp;gt;H_{k-1} - H_{k}&amp;lt;/tex&amp;gt;, получим в итоге дерево высоты не большей, чем &amp;lt;tex&amp;gt;H_{k-1}&amp;lt;/tex&amp;gt;. Его мы передадим наверх, поэтому в следующий раз слияние будет выполнено за &amp;lt;tex&amp;gt;H_{k-2} - H_{k - 1}&amp;lt;/tex&amp;gt; и так далее. Таким образом мы получим &amp;lt;tex&amp;gt;(H - H_{1}) + (H_{1} - H_{2}) + (H_{2} - H_{3}) + \cdots +  (H_{k - 1} - H_{k}) = H - H_{k} = O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итоговая асимптотика алгоритма {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [http://habrahabr.ru/post/150732/ Habrahabr {{---}} АВЛ-деревья]&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=46168</id>
		<title>АВЛ-дерево</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=46168"/>
				<updated>2015-05-10T05:41:44Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: /* Слияние двух AVL-деревьев */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''АВЛ-дерево''' {{---}} сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.&lt;br /&gt;
&lt;br /&gt;
АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г. М. Адельсона-Вельского и Е. М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962 году.&lt;br /&gt;
&lt;br /&gt;
== Высота дерева ==&lt;br /&gt;
{{Теорема &lt;br /&gt;
|statement=АВЛ-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключами имеет высоту &amp;lt;tex&amp;gt;h = O(\log N)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
||proof=&lt;br /&gt;
&lt;br /&gt;
Высоту поддерева с корнем &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; будем обозначать как &amp;lt;tex&amp;gt;h(x)&amp;lt;/tex&amp;gt;, высоту поддерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; {{---}} как &amp;lt;tex&amp;gt;h(T)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Пусть &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt;, где            &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фибоначчи.  &lt;br /&gt;
|proof=&lt;br /&gt;
Если &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;. Тогда, как легко видеть, &amp;lt;tex&amp;gt;m_{h+2} = m_{h+1} + m_h + 1&amp;lt;/tex&amp;gt;. Равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; докажем по индукции. &lt;br /&gt;
&lt;br /&gt;
База индукции &amp;lt;tex&amp;gt;m_1 = F_3 - 1&amp;lt;/tex&amp;gt; {{---}} верно, &amp;lt;tex&amp;gt;m_1 = 1, F_3 = 2&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Допустим &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} верно. &lt;br /&gt;
&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;m_{h+1} = m_h + m_{h-1} + 1 = F_{h+2} - 1 + F_{h+1} - 1 + 1 = F_{h+3} - 1&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Таким образом, равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} доказано.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\varphi = \frac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. То есть&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n \geqslant \varphi^{h}&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 h&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом,  получаем, что высота AVL-дерева из n вершин {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Балансировка ==&lt;br /&gt;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &amp;lt;tex&amp;gt;|h(L) - h(R)| = 2&amp;lt;/tex&amp;gt;, изменяет связи предок-потомок в поддереве данной вершины так, чтобы восстановилось свойство дерева &amp;lt;tex&amp;gt;|h(L) - h(R)| \leqslant 1&amp;lt;/tex&amp;gt;, иначе ничего не меняет.&lt;br /&gt;
Для балансировки будем хранить для каждой вершины разницу между высотой её левого и правого поддерева &amp;lt;tex&amp;gt;diff[i] = h(L) - h(R)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для балансировки вершины используются один из 4 типов вращений:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
 !Тип вращения&lt;br /&gt;
 !Иллюстрация&lt;br /&gt;
 !Когда используется&lt;br /&gt;
 !Расстановка балансов&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое левое вращение'''&lt;br /&gt;
 |  [[Файл:avl_u1.jpg|2000x200px]]  &lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение'''&lt;br /&gt;
 | [[Файл:avl_u2.jpg|2000x200px]]&lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; , &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 |}&lt;br /&gt;
Малое правое и большое правое вращение определяются симметрично малому левому и большому левому вращению.&lt;br /&gt;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 1 и не может увеличиться.&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;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Будем спускаться по дереву, как при поиске ключа &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Если мы стоим в вершине &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и нам надо идти в поддерево, которого нет, то делаем ключ &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; листом, а вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; его корнем. Дальше поднимаемся вверх по пути поиска и пересчитываем баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
Так как в процессе добавления вершины мы рассматриваем не более, чем &amp;lt;tex&amp;gt; O(h) &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;
Если вершина - лист, то [[Дерево поиска, наивная реализация#Удаление|удалим]] её, иначе найдём самую близкую по значению вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, переместим её на место удаляемой вершины и [[Дерево поиска, наивная реализация#Удаление|удалим]] вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. От удалённой вершины будем подниматься вверх к корню и пересчитывать баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; уменьшается на единицу, если из правого, то увеличивается на единицу. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
В результате указанных действий на удаление вершины и балансировку суммарно тратится, как и ранее, &amp;lt;tex&amp;gt; O(h) &amp;lt;/tex&amp;gt; операций. Таким образом, требуемое количество действий {{---}} &amp;lt;tex&amp;gt; O(\log{n}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Поиск вершины, минимум/максимум в дереве, etc. ===&lt;br /&gt;
Остальные операции не меняют структуры дерева, поэтому выполняются так же, как и в [[Дерево поиска, наивная реализация|наивной реализации]] дерева поиска.&lt;br /&gt;
===Слияние двух AVL-деревьев===&lt;br /&gt;
&lt;br /&gt;
Дано два дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, все ключи в &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;h(T_1) \leqslant h(T_2)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В дереве &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; удаляем самую правую вершину, назовём её &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;. Высота дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; может уменьшиться на единицу. В дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; идём от корня всегда в левое поддерево и, когда высота этого поддерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; будет равна высоте дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, делаем новое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; будет вершина &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, левым поддеревом будет дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, а правым дерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;. Теперь в дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; у вершины, в которой мы остановились при спуске, левым поддеревом делаем дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; до слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u3.jpg|340px]]&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; после слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u4.jpg|300px]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=46167</id>
		<title>АВЛ-дерево</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=46167"/>
				<updated>2015-05-10T05:41:21Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: /* Балансировка */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''АВЛ-дерево''' {{---}} сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.&lt;br /&gt;
&lt;br /&gt;
АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г. М. Адельсона-Вельского и Е. М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962 году.&lt;br /&gt;
&lt;br /&gt;
== Высота дерева ==&lt;br /&gt;
{{Теорема &lt;br /&gt;
|statement=АВЛ-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключами имеет высоту &amp;lt;tex&amp;gt;h = O(\log N)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
||proof=&lt;br /&gt;
&lt;br /&gt;
Высоту поддерева с корнем &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; будем обозначать как &amp;lt;tex&amp;gt;h(x)&amp;lt;/tex&amp;gt;, высоту поддерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; {{---}} как &amp;lt;tex&amp;gt;h(T)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Пусть &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt;, где            &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фибоначчи.  &lt;br /&gt;
|proof=&lt;br /&gt;
Если &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;. Тогда, как легко видеть, &amp;lt;tex&amp;gt;m_{h+2} = m_{h+1} + m_h + 1&amp;lt;/tex&amp;gt;. Равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; докажем по индукции. &lt;br /&gt;
&lt;br /&gt;
База индукции &amp;lt;tex&amp;gt;m_1 = F_3 - 1&amp;lt;/tex&amp;gt; {{---}} верно, &amp;lt;tex&amp;gt;m_1 = 1, F_3 = 2&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Допустим &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} верно. &lt;br /&gt;
&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;m_{h+1} = m_h + m_{h-1} + 1 = F_{h+2} - 1 + F_{h+1} - 1 + 1 = F_{h+3} - 1&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Таким образом, равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} доказано.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\varphi = \frac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. То есть&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n \geqslant \varphi^{h}&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 h&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом,  получаем, что высота AVL-дерева из n вершин {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Балансировка ==&lt;br /&gt;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &amp;lt;tex&amp;gt;|h(L) - h(R)| = 2&amp;lt;/tex&amp;gt;, изменяет связи предок-потомок в поддереве данной вершины так, чтобы восстановилось свойство дерева &amp;lt;tex&amp;gt;|h(L) - h(R)| \leqslant 1&amp;lt;/tex&amp;gt;, иначе ничего не меняет.&lt;br /&gt;
Для балансировки будем хранить для каждой вершины разницу между высотой её левого и правого поддерева &amp;lt;tex&amp;gt;diff[i] = h(L) - h(R)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для балансировки вершины используются один из 4 типов вращений:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
 !Тип вращения&lt;br /&gt;
 !Иллюстрация&lt;br /&gt;
 !Когда используется&lt;br /&gt;
 !Расстановка балансов&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое левое вращение'''&lt;br /&gt;
 |  [[Файл:avl_u1.jpg|2000x200px]]  &lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение'''&lt;br /&gt;
 | [[Файл:avl_u2.jpg|2000x200px]]&lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; , &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 |}&lt;br /&gt;
Малое правое и большое правое вращение определяются симметрично малому левому и большому левому вращению.&lt;br /&gt;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 1 и не может увеличиться.&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;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Будем спускаться по дереву, как при поиске ключа &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Если мы стоим в вершине &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и нам надо идти в поддерево, которого нет, то делаем ключ &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; листом, а вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; его корнем. Дальше поднимаемся вверх по пути поиска и пересчитываем баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
Так как в процессе добавления вершины мы рассматриваем не более, чем &amp;lt;tex&amp;gt; O(h) &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;
Если вершина - лист, то [[Дерево поиска, наивная реализация#Удаление|удалим]] её, иначе найдём самую близкую по значению вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, переместим её на место удаляемой вершины и [[Дерево поиска, наивная реализация#Удаление|удалим]] вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. От удалённой вершины будем подниматься вверх к корню и пересчитывать баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; уменьшается на единицу, если из правого, то увеличивается на единицу. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
В результате указанных действий на удаление вершины и балансировку суммарно тратится, как и ранее, &amp;lt;tex&amp;gt; O(h) &amp;lt;/tex&amp;gt; операций. Таким образом, требуемое количество действий {{---}} &amp;lt;tex&amp;gt; O(\log{n}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Поиск вершины, минимум/максимум в дереве, etc. ===&lt;br /&gt;
Остальные операции не меняют структуры дерева, поэтому выполняются так же, как и в [[Дерево поиска, наивная реализация|наивной реализации]] дерева поиска.&lt;br /&gt;
===Слияние двух AVL-деревьев===&lt;br /&gt;
&lt;br /&gt;
Дано два дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, все ключи в &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;h(T_1) \le h(T_2)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В дереве &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; удаляем самую правую вершину, назовём её &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;. Высота дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; может уменьшиться на единицу. В дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; идём от корня всегда в левое поддерево и, когда высота этого поддерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; будет равна высоте дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, делаем новое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; будет вершина &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, левым поддеревом будет дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, а правым дерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;. Теперь в дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; у вершины, в которой мы остановились при спуске, левым поддеревом делаем дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; до слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u3.jpg|340px]]&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; после слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u4.jpg|300px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=46163</id>
		<title>Персистентный стек</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=46163"/>
				<updated>2015-05-09T12:07:04Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: /* Пример */ тикет 4-3&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Эффективная реализация ==&lt;br /&gt;
&lt;br /&gt;
Попробуем решить задачу эффективнее. Заведем массив запросов, модифицирующих стек.&amp;lt;br&amp;gt;&lt;br /&gt;
У каждого элемента массива будет 2 поля: значение в вершине стека и индекс предыдущей версии стека.&amp;lt;br&amp;gt;&lt;br /&gt;
Тогда операции push и pop будут иметь следующий вид:&amp;lt;br&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt; \mathrm{push}(i, x)&amp;lt;/tex&amp;gt; {{---}} добавляет элемент х в стек с номером i, результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  function push(i : uint, x : T):&lt;br /&gt;
    s.top = s.top + 1;&lt;br /&gt;
    s[s.top].value = x;&lt;br /&gt;
    s[s.top].prev = i; &lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathrm{pop}(i)&amp;lt;/tex&amp;gt; {{---}} возвращает значение, хранящееся в элементе с номером i и копирует элемент, предыдущий для него.&lt;br /&gt;
результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  T pop(i : uint):&lt;br /&gt;
    T k = s[i];&lt;br /&gt;
    k = s[k.prev];&lt;br /&gt;
    push(k.prev, k.value);&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
&lt;br /&gt;
* Пусть изначально у нас есть один пустой стек. Запишем его в массив.&lt;br /&gt;
[[Файл:стек1.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Далее выполним &amp;lt;tex&amp;gt;\mathrm{push}(1, 3)&amp;lt;/tex&amp;gt;. Создается новая вершина со значением 3, ссылающаяся на 1-ую, помещаем ее во 2-ую ячейку массива:&lt;br /&gt;
[[Файл:стек2.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Аналогично выполним &amp;lt;tex&amp;gt;\mathrm{push}(2, 5)&amp;lt;/tex&amp;gt;:&lt;br /&gt;
[[Файл:стек3.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Выполним &amp;lt;tex&amp;gt;\mathrm{pop}(3)&amp;lt;/tex&amp;gt;. он возвращает 5 и копирует 2-ую вершину.&lt;br /&gt;
[[Файл:стек4.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Так будет выглядеть массив после последовательности операций &amp;lt;tex&amp;gt;\mathrm{push}(3, 6),  \mathrm{push}(5, 1),  \mathrm{pop}(4),  \mathrm{pop}(5),  \mathrm{push}(7, 9):&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:стек.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;8&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;9&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|1&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|5&lt;br /&gt;
|9&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|2&lt;br /&gt;
|7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В итоге мы имеем доступ ко всем версиям стека за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; времени и &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; памяти.&lt;br /&gt;
&lt;br /&gt;
== См. также==&lt;br /&gt;
&lt;br /&gt;
* [[Стек]]&lt;br /&gt;
* [[Персистентный дек]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
&lt;br /&gt;
* [http://habrahabr.ru/blogs/algorithm/113585/ Habrahabr - Персистентный стек]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Амортизационный анализ]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=46161</id>
		<title>Персистентный стек</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=46161"/>
				<updated>2015-05-09T12:02:16Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: /* Эффективная реализация */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Эффективная реализация ==&lt;br /&gt;
&lt;br /&gt;
Попробуем решить задачу эффективнее. Заведем массив запросов, модифицирующих стек.&amp;lt;br&amp;gt;&lt;br /&gt;
У каждого элемента массива будет 2 поля: значение в вершине стека и индекс предыдущей версии стека.&amp;lt;br&amp;gt;&lt;br /&gt;
Тогда операции push и pop будут иметь следующий вид:&amp;lt;br&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt; \mathrm{push}(i, x)&amp;lt;/tex&amp;gt; {{---}} добавляет элемент х в стек с номером i, результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  function push(i : uint, x : T):&lt;br /&gt;
    s.top = s.top + 1;&lt;br /&gt;
    s[s.top].value = x;&lt;br /&gt;
    s[s.top].prev = i; &lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathrm{pop}(i)&amp;lt;/tex&amp;gt; {{---}} возвращает значение, хранящееся в элементе с номером i и копирует элемент, предыдущий для него.&lt;br /&gt;
результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  T pop(i : uint):&lt;br /&gt;
    T k = s[i];&lt;br /&gt;
    k = s[k.prev];&lt;br /&gt;
    push(k.prev, k.value);&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
&lt;br /&gt;
* Пусть изначально у нас есть один пустой стек. Запишем его в массив.&lt;br /&gt;
[[Файл:стек1.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Далее выполним &amp;lt;tex&amp;gt;push(1, 3)&amp;lt;/tex&amp;gt;. Создается новая вершина со значением 3, ссылающаяся на 1-ую, помещаем ее во 2-ую ячейку массива:&lt;br /&gt;
[[Файл:стек2.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Аналогично выполним &amp;lt;tex&amp;gt;push(2, 5)&amp;lt;/tex&amp;gt;:&lt;br /&gt;
[[Файл:стек3.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Выполним &amp;lt;tex&amp;gt;pop(3)&amp;lt;/tex&amp;gt;. он возвращает 5 и копирует 2-ую вершину.&lt;br /&gt;
[[Файл:стек4.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Так будет выглядеть массив после последовательности операций &amp;lt;tex&amp;gt;push(3, 6),  push(5, 1),  pop(4),  pop(5),  push(7, 9):&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:стек.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;8&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;9&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|1&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|5&lt;br /&gt;
|9&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|2&lt;br /&gt;
|7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В итоге мы имеем доступ ко всем версиям стека за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; времени и &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; памяти.&lt;br /&gt;
&lt;br /&gt;
&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;
* [http://habrahabr.ru/blogs/algorithm/113585/ Habrahabr - Персистентный стек]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Амортизационный анализ]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=46160</id>
		<title>Персистентный стек</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=46160"/>
				<updated>2015-05-09T12:00:21Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: /* Эффективная реализация */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Эффективная реализация ==&lt;br /&gt;
&lt;br /&gt;
Попробуем решить задачу эффективнее. Заведем массив запросов, модифицирующих стек.&amp;lt;br&amp;gt;&lt;br /&gt;
У каждого элемента массива будет 2 поля: значение в вершине стека и индекс предыдущей версии стека.&amp;lt;br&amp;gt;&lt;br /&gt;
Тогда операции push и pop будут иметь следующий вид:&amp;lt;br&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;push(i, x)&amp;lt;/tex&amp;gt; {{---}} добавляет элемент х в стек с номером i, результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  function push(i : uint, x : T):&lt;br /&gt;
    s.top = s.top + 1;&lt;br /&gt;
    s[s.top].value = x;&lt;br /&gt;
    s[s.top].prev = i; &lt;br /&gt;
* &amp;lt;tex&amp;gt;pop(i)&amp;lt;/tex&amp;gt; {{---}} возвращает значение, хранящееся в элементе с номером i и копирует элемент, предыдущий для него.&lt;br /&gt;
результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  T pop(i : uint):&lt;br /&gt;
    T k = s[i];&lt;br /&gt;
    k = s[k.prev];&lt;br /&gt;
    push(k.prev, k.value);&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
&lt;br /&gt;
* Пусть изначально у нас есть один пустой стек. Запишем его в массив.&lt;br /&gt;
[[Файл:стек1.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Далее выполним &amp;lt;tex&amp;gt;push(1, 3)&amp;lt;/tex&amp;gt;. Создается новая вершина со значением 3, ссылающаяся на 1-ую, помещаем ее во 2-ую ячейку массива:&lt;br /&gt;
[[Файл:стек2.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Аналогично выполним &amp;lt;tex&amp;gt;push(2, 5)&amp;lt;/tex&amp;gt;:&lt;br /&gt;
[[Файл:стек3.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Выполним &amp;lt;tex&amp;gt;pop(3)&amp;lt;/tex&amp;gt;. он возвращает 5 и копирует 2-ую вершину.&lt;br /&gt;
[[Файл:стек4.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Так будет выглядеть массив после последовательности операций &amp;lt;tex&amp;gt;push(3, 6),  push(5, 1),  pop(4),  pop(5),  push(7, 9):&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:стек.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;8&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;9&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|1&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|5&lt;br /&gt;
|9&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|2&lt;br /&gt;
|7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В итоге мы имеем доступ ко всем версиям стека за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; времени и &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; памяти.&lt;br /&gt;
&lt;br /&gt;
&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;
* [http://habrahabr.ru/blogs/algorithm/113585/ Habrahabr - Персистентный стек]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Амортизационный анализ]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=46159</id>
		<title>Персистентный стек</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=46159"/>
				<updated>2015-05-09T11:58:19Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: /* Эффективная реализация */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Эффективная реализация ==&lt;br /&gt;
&lt;br /&gt;
Попробуем решить задачу эффективнее. Заведем массив запросов, модифицирующих стек.&amp;lt;br&amp;gt;&lt;br /&gt;
У каждого элемента массива будет 2 поля: значение в вершине стека и индекс предыдущей версии стека.&amp;lt;br&amp;gt;&lt;br /&gt;
Тогда операции push и pop будут иметь следующий вид:&amp;lt;br&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;push(i, x)&amp;lt;/tex&amp;gt; — добавляет элемент х в стек с номером i, результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  function push(i : uint, x : T):&lt;br /&gt;
    s.top = s.top + 1;&lt;br /&gt;
    s[s.top].value = x;&lt;br /&gt;
    s[s.top].prev = i; &lt;br /&gt;
* &amp;lt;tex&amp;gt;pop(i)&amp;lt;/tex&amp;gt; — возвращает значение, хранящееся в элементе с номером i и копирует элемент, предыдущий для него.&lt;br /&gt;
результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  T pop(i : uint):&lt;br /&gt;
    T k = s[i];&lt;br /&gt;
    k = s[k.prev];&lt;br /&gt;
    push(k.prev, k.value);&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
&lt;br /&gt;
* Пусть изначально у нас есть один пустой стек. Запишем его в массив.&lt;br /&gt;
[[Файл:стек1.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Далее выполним &amp;lt;tex&amp;gt;push(1, 3)&amp;lt;/tex&amp;gt;. Создается новая вершина со значением 3, ссылающаяся на 1-ую, помещаем ее во 2-ую ячейку массива:&lt;br /&gt;
[[Файл:стек2.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Аналогично выполним &amp;lt;tex&amp;gt;push(2, 5)&amp;lt;/tex&amp;gt;:&lt;br /&gt;
[[Файл:стек3.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Выполним &amp;lt;tex&amp;gt;pop(3)&amp;lt;/tex&amp;gt;. он возвращает 5 и копирует 2-ую вершину.&lt;br /&gt;
[[Файл:стек4.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Так будет выглядеть массив после последовательности операций &amp;lt;tex&amp;gt;push(3, 6),  push(5, 1),  pop(4),  pop(5),  push(7, 9):&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:стек.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;8&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;9&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|1&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|5&lt;br /&gt;
|9&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|2&lt;br /&gt;
|7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В итоге мы имеем доступ ко всем версиям стека за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; времени и &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; памяти.&lt;br /&gt;
&lt;br /&gt;
&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;
* [http://habrahabr.ru/blogs/algorithm/113585/ Habrahabr - Персистентный стек]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Амортизационный анализ]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=46158</id>
		<title>Персистентный стек</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=46158"/>
				<updated>2015-05-09T11:47:33Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: тикет 4-3&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Эффективная реализация ==&lt;br /&gt;
&lt;br /&gt;
Попробуем решить задачу эффективнее. Заведем массив запросов, модифицирующих стек.&amp;lt;br&amp;gt;&lt;br /&gt;
У каждого элемента массива будет 2 поля: значение в вершине стека и индекс предыдущей версии стека.&amp;lt;br&amp;gt;&lt;br /&gt;
Тогда операции push и pop будут иметь следующий вид:&amp;lt;br&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;push(i, x)&amp;lt;/tex&amp;gt; — добавляет элемент х в стек с номером i, результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  arr.push (x, i)&lt;br /&gt;
* &amp;lt;tex&amp;gt;pop(i)&amp;lt;/tex&amp;gt; — возвращает значение, хранящееся в элементе с номером i и копирует элемент, предыдущий для него.&lt;br /&gt;
результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  a = arr[прошлый элемент для i-ого];&lt;br /&gt;
  arr.push(a);&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
&lt;br /&gt;
* Пусть изначально у нас есть один пустой стек. Запишем его в массив.&lt;br /&gt;
[[Файл:стек1.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Далее выполним &amp;lt;tex&amp;gt;push(1, 3)&amp;lt;/tex&amp;gt;. Создается новая вершина со значением 3, ссылающаяся на 1-ую, помещаем ее во 2-ую ячейку массива:&lt;br /&gt;
[[Файл:стек2.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Аналогично выполним &amp;lt;tex&amp;gt;push(2, 5)&amp;lt;/tex&amp;gt;:&lt;br /&gt;
[[Файл:стек3.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Выполним &amp;lt;tex&amp;gt;pop(3)&amp;lt;/tex&amp;gt;. он возвращает 5 и копирует 2-ую вершину.&lt;br /&gt;
[[Файл:стек4.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Так будет выглядеть массив после последовательности операций &amp;lt;tex&amp;gt;push(3, 6),  push(5, 1),  pop(4),  pop(5),  push(7, 9):&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:стек.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;8&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;9&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|1&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|5&lt;br /&gt;
|9&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|2&lt;br /&gt;
|7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В итоге мы имеем доступ ко всем версиям стека за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; времени и &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; памяти.&lt;br /&gt;
&lt;br /&gt;
&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;
* [http://habrahabr.ru/blogs/algorithm/113585/ Habrahabr - Персистентный стек]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Амортизационный анализ]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=46157</id>
		<title>Персистентный стек</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=46157"/>
				<updated>2015-05-09T11:46:45Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: /* Ссылки */ тикет 4-3&lt;/p&gt;
&lt;hr /&gt;
&lt;div&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;br&amp;gt;&lt;br /&gt;
Очевидно, это не самое эффективное решение. Сложность одной операции составляет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; и количество требуемой памяти — &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Эффективная реализация ==&lt;br /&gt;
&lt;br /&gt;
Попробуем решить задачу эффективнее. Заведем массив запросов, модифицирующих стек.&amp;lt;br&amp;gt;&lt;br /&gt;
У каждого элемента массива будет 2 поля: значение в вершине стека и индекс предыдущей версии стека.&amp;lt;br&amp;gt;&lt;br /&gt;
Тогда операции push и pop будут иметь следующий вид:&amp;lt;br&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;push(i, x)&amp;lt;/tex&amp;gt; — добавляет элемент х в стек с номером i, результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  arr.push (x, i)&lt;br /&gt;
* &amp;lt;tex&amp;gt;pop(i)&amp;lt;/tex&amp;gt; — возвращает значение, хранящееся в элементе с номером i и копирует элемент, предыдущий для него.&lt;br /&gt;
результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  a = arr[прошлый элемент для i-ого];&lt;br /&gt;
  arr.push(a);&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
&lt;br /&gt;
* Пусть изначально у нас есть один пустой стек. Запишем его в массив.&lt;br /&gt;
[[Файл:стек1.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Далее выполним &amp;lt;tex&amp;gt;push(1, 3)&amp;lt;/tex&amp;gt;. Создается новая вершина со значением 3, ссылающаяся на 1-ую, помещаем ее во 2-ую ячейку массива:&lt;br /&gt;
[[Файл:стек2.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Аналогично выполним &amp;lt;tex&amp;gt;push(2, 5)&amp;lt;/tex&amp;gt;:&lt;br /&gt;
[[Файл:стек3.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Выполним &amp;lt;tex&amp;gt;pop(3)&amp;lt;/tex&amp;gt;. он возвращает 5 и копирует 2-ую вершину.&lt;br /&gt;
[[Файл:стек4.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Так будет выглядеть массив после последовательности операций &amp;lt;tex&amp;gt;push(3, 6),  push(5, 1),  pop(4),  pop(5),  push(7, 9):&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:стек.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;8&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;9&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|1&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|5&lt;br /&gt;
|9&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|2&lt;br /&gt;
|7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В итоге мы имеем доступ ко всем версиям стека за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; времени и &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; памяти.&lt;br /&gt;
&lt;br /&gt;
&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;
* [http://habrahabr.ru/blogs/algorithm/113585/ Habrahabr - Персистентный стек]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Амортизационный анализ]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=46156</id>
		<title>АВЛ-дерево</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=46156"/>
				<updated>2015-05-09T11:40:51Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: /* Литература */ тикет 4-3&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''АВЛ-дерево''' {{---}} сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.&lt;br /&gt;
&lt;br /&gt;
АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г. М. Адельсона-Вельского и Е. М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962 году.&lt;br /&gt;
&lt;br /&gt;
== Высота дерева ==&lt;br /&gt;
{{Теорема &lt;br /&gt;
|statement=АВЛ-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключами имеет высоту &amp;lt;tex&amp;gt;h = O(\log N)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
||proof=&lt;br /&gt;
&lt;br /&gt;
Высоту поддерева с корнем &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; будем обозначать как &amp;lt;tex&amp;gt;h(x)&amp;lt;/tex&amp;gt;, высоту поддерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; {{---}} как &amp;lt;tex&amp;gt;h(T)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Пусть &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt;, где            &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фибоначчи.  &lt;br /&gt;
|proof=&lt;br /&gt;
Если &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;. Тогда, как легко видеть, &amp;lt;tex&amp;gt;m_{h+2} = m_{h+1} + m_h + 1&amp;lt;/tex&amp;gt;. Равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; докажем по индукции. &lt;br /&gt;
&lt;br /&gt;
База индукции &amp;lt;tex&amp;gt;m_1 = F_3 - 1&amp;lt;/tex&amp;gt; {{---}} верно, &amp;lt;tex&amp;gt;m_1 = 1, F_3 = 2&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Допустим &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} верно. &lt;br /&gt;
&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;m_{h+1} = m_h + m_{h-1} + 1 = F_{h+2} - 1 + F_{h+1} - 1 + 1 = F_{h+3} - 1&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Таким образом, равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} доказано.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\varphi = \frac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. То есть&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n \geqslant \varphi^{h}&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 h&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом,  получаем, что высота AVL-дерева из n вершин {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Балансировка ==&lt;br /&gt;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &amp;lt;tex&amp;gt;|h(L) - h(R)| = 2&amp;lt;/tex&amp;gt;, изменяет связи предок-потомок в поддереве данной вершины так, чтобы восстановилось свойство дерева &amp;lt;tex&amp;gt;|h(L) - h(R)| \le 1&amp;lt;/tex&amp;gt;, иначе ничего не меняет.&lt;br /&gt;
Для балансировки будем хранить для каждой вершины разницу между высотой её левого и правого поддерева &amp;lt;tex&amp;gt;diff[i] = h(L) - h(R)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для балансировки вершины используются один из 4 типов вращений:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
 !Тип вращения&lt;br /&gt;
 !Иллюстрация&lt;br /&gt;
 !Когда используется&lt;br /&gt;
 !Расстановка балансов&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое левое вращение'''&lt;br /&gt;
 |  [[Файл:avl_u1.jpg|2000x200px]]  &lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение'''&lt;br /&gt;
 | [[Файл:avl_u2.jpg|2000x200px]]&lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; , &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 |}&lt;br /&gt;
Малое правое и большое правое вращение определяются симметрично малому левому и большому левому вращению.&lt;br /&gt;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 1 и не может увеличиться.&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;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Будем спускаться по дереву, как при поиске ключа &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Если мы стоим в вершине &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и нам надо идти в поддерево, которого нет, то делаем ключ &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; листом, а вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; его корнем. Дальше поднимаемся вверх по пути поиска и пересчитываем баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
Так как в процессе добавления вершины мы рассматриваем не более, чем &amp;lt;tex&amp;gt; O(h) &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;
Если вершина - лист, то [[Дерево поиска, наивная реализация#Удаление|удалим]] её, иначе найдём самую близкую по значению вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, переместим её на место удаляемой вершины и [[Дерево поиска, наивная реализация#Удаление|удалим]] вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. От удалённой вершины будем подниматься вверх к корню и пересчитывать баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; уменьшается на единицу, если из правого, то увеличивается на единицу. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
В результате указанных действий на удаление вершины и балансировку суммарно тратится, как и ранее, &amp;lt;tex&amp;gt; O(h) &amp;lt;/tex&amp;gt; операций. Таким образом, требуемое количество действий {{---}} &amp;lt;tex&amp;gt; O(\log{n}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Поиск вершины, минимум/максимум в дереве, etc. ===&lt;br /&gt;
Остальные операции не меняют структуры дерева, поэтому выполняются так же, как и в [[Дерево поиска, наивная реализация|наивной реализации]] дерева поиска.&lt;br /&gt;
===Слияние двух AVL-деревьев===&lt;br /&gt;
&lt;br /&gt;
Дано два дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, все ключи в &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;h(T_1) \le h(T_2)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В дереве &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; удаляем самую правую вершину, назовём её &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;. Высота дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; может уменьшиться на единицу. В дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; идём от корня всегда в левое поддерево и, когда высота этого поддерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; будет равна высоте дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, делаем новое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; будет вершина &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, левым поддеревом будет дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, а правым дерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;. Теперь в дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; у вершины, в которой мы остановились при спуске, левым поддеревом делаем дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; до слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u3.jpg|340px]]&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; после слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u4.jpg|300px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=46155</id>
		<title>АВЛ-дерево</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=46155"/>
				<updated>2015-05-09T11:40:05Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: /* Удаление вершины */ тикет 4-3&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''АВЛ-дерево''' {{---}} сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.&lt;br /&gt;
&lt;br /&gt;
АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г. М. Адельсона-Вельского и Е. М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962 году.&lt;br /&gt;
&lt;br /&gt;
== Высота дерева ==&lt;br /&gt;
{{Теорема &lt;br /&gt;
|statement=АВЛ-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключами имеет высоту &amp;lt;tex&amp;gt;h = O(\log N)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
||proof=&lt;br /&gt;
&lt;br /&gt;
Высоту поддерева с корнем &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; будем обозначать как &amp;lt;tex&amp;gt;h(x)&amp;lt;/tex&amp;gt;, высоту поддерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; {{---}} как &amp;lt;tex&amp;gt;h(T)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Пусть &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt;, где            &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фибоначчи.  &lt;br /&gt;
|proof=&lt;br /&gt;
Если &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;. Тогда, как легко видеть, &amp;lt;tex&amp;gt;m_{h+2} = m_{h+1} + m_h + 1&amp;lt;/tex&amp;gt;. Равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; докажем по индукции. &lt;br /&gt;
&lt;br /&gt;
База индукции &amp;lt;tex&amp;gt;m_1 = F_3 - 1&amp;lt;/tex&amp;gt; {{---}} верно, &amp;lt;tex&amp;gt;m_1 = 1, F_3 = 2&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Допустим &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} верно. &lt;br /&gt;
&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;m_{h+1} = m_h + m_{h-1} + 1 = F_{h+2} - 1 + F_{h+1} - 1 + 1 = F_{h+3} - 1&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Таким образом, равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} доказано.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\varphi = \frac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. То есть&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n \geqslant \varphi^{h}&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 h&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом,  получаем, что высота AVL-дерева из n вершин {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Балансировка ==&lt;br /&gt;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &amp;lt;tex&amp;gt;|h(L) - h(R)| = 2&amp;lt;/tex&amp;gt;, изменяет связи предок-потомок в поддереве данной вершины так, чтобы восстановилось свойство дерева &amp;lt;tex&amp;gt;|h(L) - h(R)| \le 1&amp;lt;/tex&amp;gt;, иначе ничего не меняет.&lt;br /&gt;
Для балансировки будем хранить для каждой вершины разницу между высотой её левого и правого поддерева &amp;lt;tex&amp;gt;diff[i] = h(L) - h(R)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для балансировки вершины используются один из 4 типов вращений:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
 !Тип вращения&lt;br /&gt;
 !Иллюстрация&lt;br /&gt;
 !Когда используется&lt;br /&gt;
 !Расстановка балансов&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое левое вращение'''&lt;br /&gt;
 |  [[Файл:avl_u1.jpg|2000x200px]]  &lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение'''&lt;br /&gt;
 | [[Файл:avl_u2.jpg|2000x200px]]&lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; , &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 |}&lt;br /&gt;
Малое правое и большое правое вращение определяются симметрично малому левому и большому левому вращению.&lt;br /&gt;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 1 и не может увеличиться.&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;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Будем спускаться по дереву, как при поиске ключа &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Если мы стоим в вершине &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и нам надо идти в поддерево, которого нет, то делаем ключ &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; листом, а вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; его корнем. Дальше поднимаемся вверх по пути поиска и пересчитываем баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
Так как в процессе добавления вершины мы рассматриваем не более, чем &amp;lt;tex&amp;gt; O(h) &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;
Если вершина - лист, то [[Дерево поиска, наивная реализация#Удаление|удалим]] её, иначе найдём самую близкую по значению вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, переместим её на место удаляемой вершины и [[Дерево поиска, наивная реализация#Удаление|удалим]] вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. От удалённой вершины будем подниматься вверх к корню и пересчитывать баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; уменьшается на единицу, если из правого, то увеличивается на единицу. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
В результате указанных действий на удаление вершины и балансировку суммарно тратится, как и ранее, &amp;lt;tex&amp;gt; O(h) &amp;lt;/tex&amp;gt; операций. Таким образом, требуемое количество действий {{---}} &amp;lt;tex&amp;gt; O(\log{n}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Поиск вершины, минимум/максимум в дереве, etc. ===&lt;br /&gt;
Остальные операции не меняют структуры дерева, поэтому выполняются так же, как и в [[Дерево поиска, наивная реализация|наивной реализации]] дерева поиска.&lt;br /&gt;
===Слияние двух AVL-деревьев===&lt;br /&gt;
&lt;br /&gt;
Дано два дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, все ключи в &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;h(T_1) \le h(T_2)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В дереве &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; удаляем самую правую вершину, назовём её &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;. Высота дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; может уменьшиться на единицу. В дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; идём от корня всегда в левое поддерево и, когда высота этого поддерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; будет равна высоте дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, делаем новое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; будет вершина &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, левым поддеревом будет дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, а правым дерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;. Теперь в дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; у вершины, в которой мы остановились при спуске, левым поддеревом делаем дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; до слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u3.jpg|340px]]&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; после слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u4.jpg|300px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=46154</id>
		<title>АВЛ-дерево</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%92%D0%9B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=46154"/>
				<updated>2015-05-09T11:39:13Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: /* Добавление вершины */ тикет 4-3&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''АВЛ-дерево''' {{---}} сбалансированное [[Дерево поиска, наивная реализация|двоичное дерево поиска]], в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.&lt;br /&gt;
&lt;br /&gt;
АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г. М. Адельсона-Вельского и Е. М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962 году.&lt;br /&gt;
&lt;br /&gt;
== Высота дерева ==&lt;br /&gt;
{{Теорема &lt;br /&gt;
|statement=АВЛ-дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключами имеет высоту &amp;lt;tex&amp;gt;h = O(\log N)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
||proof=&lt;br /&gt;
&lt;br /&gt;
Высоту поддерева с корнем &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; будем обозначать как &amp;lt;tex&amp;gt;h(x)&amp;lt;/tex&amp;gt;, высоту поддерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; {{---}} как &amp;lt;tex&amp;gt;h(T)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Пусть &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt;, где            &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фибоначчи.  &lt;br /&gt;
|proof=&lt;br /&gt;
Если &amp;lt;tex&amp;gt;m_h&amp;lt;/tex&amp;gt; {{---}} минимальное число вершин в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt;. Тогда, как легко видеть, &amp;lt;tex&amp;gt;m_{h+2} = m_{h+1} + m_h + 1&amp;lt;/tex&amp;gt;. Равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; докажем по индукции. &lt;br /&gt;
&lt;br /&gt;
База индукции &amp;lt;tex&amp;gt;m_1 = F_3 - 1&amp;lt;/tex&amp;gt; {{---}} верно, &amp;lt;tex&amp;gt;m_1 = 1, F_3 = 2&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Допустим &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} верно. &lt;br /&gt;
&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;m_{h+1} = m_h + m_{h-1} + 1 = F_{h+2} - 1 + F_{h+1} - 1 + 1 = F_{h+3} - 1&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Таким образом, равенство &amp;lt;tex&amp;gt;m_h = F_{h+2} - 1&amp;lt;/tex&amp;gt; {{---}} доказано.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\varphi = \frac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. То есть&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;n \geqslant \varphi^{h}&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 h&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом,  получаем, что высота AVL-дерева из n вершин {{---}} &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Балансировка ==&lt;br /&gt;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &amp;lt;tex&amp;gt;|h(L) - h(R)| = 2&amp;lt;/tex&amp;gt;, изменяет связи предок-потомок в поддереве данной вершины так, чтобы восстановилось свойство дерева &amp;lt;tex&amp;gt;|h(L) - h(R)| \le 1&amp;lt;/tex&amp;gt;, иначе ничего не меняет.&lt;br /&gt;
Для балансировки будем хранить для каждой вершины разницу между высотой её левого и правого поддерева &amp;lt;tex&amp;gt;diff[i] = h(L) - h(R)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для балансировки вершины используются один из 4 типов вращений:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
 !Тип вращения&lt;br /&gt;
 !Иллюстрация&lt;br /&gt;
 !Когда используется&lt;br /&gt;
 !Расстановка балансов&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое левое вращение'''&lt;br /&gt;
 |  [[Файл:avl_u1.jpg|2000x200px]]  &lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение'''&lt;br /&gt;
 | [[Файл:avl_u2.jpg|2000x200px]]&lt;br /&gt;
 |&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt; , &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = -2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = -1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;diff[a] = 0&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;diff[b] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;diff[c] = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 |}&lt;br /&gt;
Малое правое и большое правое вращение определяются симметрично малому левому и большому левому вращению.&lt;br /&gt;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 1 и не может увеличиться.&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;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Будем спускаться по дереву, как при поиске ключа &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Если мы стоим в вершине &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и нам надо идти в поддерево, которого нет, то делаем ключ &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; листом, а вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; его корнем. Дальше поднимаемся вверх по пути поиска и пересчитываем баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;-1&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;
Так как в процессе добавления вершины мы рассматриваем не более, чем &amp;lt;tex&amp;gt; O(h) &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;
Если вершина - лист, то [[Дерево поиска, наивная реализация#Удаление|удалим]] её, иначе найдём самую близкую по значению вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, переместим её на место удаляемой вершины и [[Дерево поиска, наивная реализация#Удаление|удалим]] вершину &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. От удалённой вершины будем подниматься вверх к корню и пересчитывать баланс у вершин. Если мы поднялись в вершину &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; из левого поддерева, то &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; уменьшается на единицу, если из правого, то увеличивается на единицу. Если пришли в вершину и её баланс стал равным 1 или -1, то это значит, что высота этого поддерева не изменилась и подъём можно остановить. Если баланс вершины стал равным нулю, то высота поддерева уменьшилась и подъём нужно продолжить. Если баланс стал равным 2 или -2, следует выполнить одно из четырёх вращений и, если после вращений баланс вершины стал равным нулю, то подъём продолжается, иначе останавливается.&lt;br /&gt;
&lt;br /&gt;
В результате указанных действий на удаление вершины и балансировку суммарно тратится, как и ранее, &amp;lt;tex&amp;gt; O(h) &amp;lt;/tex&amp;gt; операций. Таким образом, требуемое количество действий {{---}} &amp;lt;tex&amp;gt; O(\log{n}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Поиск вершины, минимум/максимум в дереве, etc. ===&lt;br /&gt;
Остальные операции не меняют структуры дерева, поэтому выполняются так же, как и в [[Дерево поиска, наивная реализация|наивной реализации]] дерева поиска.&lt;br /&gt;
===Слияние двух AVL-деревьев===&lt;br /&gt;
&lt;br /&gt;
Дано два дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, все ключи в &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; меньше ключей в &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;h(T_1) \le h(T_2)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В дереве &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; удаляем самую правую вершину, назовём её &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;. Высота дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; может уменьшиться на единицу. В дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; идём от корня всегда в левое поддерево и, когда высота этого поддерева &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt; будет равна высоте дерева &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, делаем новое дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; будет вершина &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, левым поддеревом будет дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt;, а правым дерево &amp;lt;tex&amp;gt;P&amp;lt;/tex&amp;gt;. Теперь в дереве &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; у вершины, в которой мы остановились при спуске, левым поддеревом делаем дерево &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; до слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u3.jpg|340px]]&lt;br /&gt;
&lt;br /&gt;
Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; после слияния&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:avl_u4.jpg|300px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=46151</id>
		<title>Персистентный стек</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=46151"/>
				<updated>2015-05-09T11:33:03Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: /* Эффективная реализация */ тикет 1-1&lt;/p&gt;
&lt;hr /&gt;
&lt;div&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;br&amp;gt;&lt;br /&gt;
Очевидно, это не самое эффективное решение. Сложность одной операции составляет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; и количество требуемой памяти — &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Эффективная реализация ==&lt;br /&gt;
&lt;br /&gt;
Попробуем решить задачу эффективнее. Заведем массив запросов, модифицирующих стек.&amp;lt;br&amp;gt;&lt;br /&gt;
У каждого элемента массива будет 2 поля: значение в вершине стека и индекс предыдущей версии стека.&amp;lt;br&amp;gt;&lt;br /&gt;
Тогда операции push и pop будут иметь следующий вид:&amp;lt;br&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;push(i, x)&amp;lt;/tex&amp;gt; — добавляет элемент х в стек с номером i, результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  arr.push (x, i)&lt;br /&gt;
* &amp;lt;tex&amp;gt;pop(i)&amp;lt;/tex&amp;gt; — возвращает значение, хранящееся в элементе с номером i и копирует элемент, предыдущий для него.&lt;br /&gt;
результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  a = arr[прошлый элемент для i-ого];&lt;br /&gt;
  arr.push(a);&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
&lt;br /&gt;
* Пусть изначально у нас есть один пустой стек. Запишем его в массив.&lt;br /&gt;
[[Файл:стек1.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Далее выполним &amp;lt;tex&amp;gt;push(1, 3)&amp;lt;/tex&amp;gt;. Создается новая вершина со значением 3, ссылающаяся на 1-ую, помещаем ее во 2-ую ячейку массива:&lt;br /&gt;
[[Файл:стек2.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Аналогично выполним &amp;lt;tex&amp;gt;push(2, 5)&amp;lt;/tex&amp;gt;:&lt;br /&gt;
[[Файл:стек3.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Выполним &amp;lt;tex&amp;gt;pop(3)&amp;lt;/tex&amp;gt;. он возвращает 5 и копирует 2-ую вершину.&lt;br /&gt;
[[Файл:стек4.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Так будет выглядеть массив после последовательности операций &amp;lt;tex&amp;gt;push(3, 6),  push(5, 1),  pop(4),  pop(5),  push(7, 9):&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:стек.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;8&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;9&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|1&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|5&lt;br /&gt;
|9&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|2&lt;br /&gt;
|7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В итоге мы имеем доступ ко всем версиям стека за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; времени и &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; памяти.&lt;br /&gt;
&lt;br /&gt;
&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;
* [http://habrahabr.ru/blogs/algorithm/113585/ Habrahabr - Персистентный стек]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Амортизационный анализ]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=45847</id>
		<title>Персистентный стек</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%B5%D1%80%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D0%B5%D0%BA&amp;diff=45847"/>
				<updated>2015-04-28T17:19:35Z</updated>
		
		<summary type="html">&lt;p&gt;217.71.235.207: Тикет 1-1-3&lt;/p&gt;
&lt;hr /&gt;
&lt;div&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;br&amp;gt;&lt;br /&gt;
Очевидно, это не самое эффективное решение. Сложность одной операции составляет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; и количество требуемой памяти — &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Эффективная реализация ==&lt;br /&gt;
&lt;br /&gt;
Попробуем решить задачу эффективнее. Заведем массив запросов, модифицирующих стек.&amp;lt;br&amp;gt;&lt;br /&gt;
У каждого элемента массива будет 2 поля: значение в вершине стека и индекс предыдущей версии стека.&amp;lt;br&amp;gt;&lt;br /&gt;
Тогда операции push и pop будут иметь следующий вид:&amp;lt;br&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;push(i, x)&amp;lt;/tex&amp;gt; — добавляет элемент х в стек с номером i, результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  mas.push_back(value = x, prev = i)&lt;br /&gt;
* &amp;lt;tex&amp;gt;pop(i)&amp;lt;/tex&amp;gt; — возвращает значение, хранящееся в элементе с номером i и копирует элемент, предыдущий для него.&lt;br /&gt;
результирующий стек будет иметь номер &amp;lt;tex&amp;gt; n + 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
  mas.push_back( copy_of(mas[i.prev]) )&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
&lt;br /&gt;
* Пусть изначально у нас есть один пустой стек. Запишем его в массив.&lt;br /&gt;
[[Файл:стек1.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Далее выполним &amp;lt;tex&amp;gt;push(1, 3)&amp;lt;/tex&amp;gt;. Создается новая вершина со значением 3, ссылающаяся на 1-ую, помещаем ее во 2-ую ячейку массива:&lt;br /&gt;
[[Файл:стек2.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Аналогично выполним &amp;lt;tex&amp;gt;push(2, 5)&amp;lt;/tex&amp;gt;:&lt;br /&gt;
[[Файл:стек3.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Выполним &amp;lt;tex&amp;gt;pop(3)&amp;lt;/tex&amp;gt;. он возвращает 5 и копирует 2-ую вершину.&lt;br /&gt;
[[Файл:стек4.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Так будет выглядеть массив после последовательности операций &amp;lt;tex&amp;gt;push(3, 6),  push(5, 1),  pop(4),  pop(5),  push(7, 9):&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:стек.png|500px|nothumb|right|]]&lt;br /&gt;
{| border = 1; cellspacing = 0; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- align = &amp;quot;center&amp;quot;&lt;br /&gt;
!index&lt;br /&gt;
!1&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;2&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;3&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;4&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;5&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;6&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;7&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;8&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
!&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;9&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!value&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|3&lt;br /&gt;
|6&lt;br /&gt;
|1&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|5&lt;br /&gt;
|9&lt;br /&gt;
|-align = &amp;quot;center&amp;quot;&lt;br /&gt;
!prev&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|3&lt;br /&gt;
|5&lt;br /&gt;
|&amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;&lt;br /&gt;
|2&lt;br /&gt;
|7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В итоге мы имеем доступ ко всем версиям стека за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; времени и &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; памяти.&lt;br /&gt;
&lt;br /&gt;
&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;
* [http://habrahabr.ru/blogs/algorithm/113585/ Habrahabr - Персистентный стек]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Амортизационный анализ]]&lt;/div&gt;</summary>
		<author><name>217.71.235.207</name></author>	</entry>

	</feed>