<?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=Georgy+Konoplich</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=Georgy+Konoplich"/>
		<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/Georgy_Konoplich"/>
		<updated>2026-05-19T17:54:50Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Avl_u3.jpg&amp;diff=26882</id>
		<title>Файл:Avl u3.jpg</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Avl_u3.jpg&amp;diff=26882"/>
				<updated>2012-06-24T22:31:09Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: загружена новая версия «Файл:Avl u3.jpg»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Avl_u4.jpg&amp;diff=26877</id>
		<title>Файл:Avl u4.jpg</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Avl_u4.jpg&amp;diff=26877"/>
				<updated>2012-06-24T21:29:56Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Avl_u3.jpg&amp;diff=26876</id>
		<title>Файл:Avl u3.jpg</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Avl_u3.jpg&amp;diff=26876"/>
				<updated>2012-06-24T21:28:31Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Avl_u2.jpg&amp;diff=26875</id>
		<title>Файл:Avl u2.jpg</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Avl_u2.jpg&amp;diff=26875"/>
				<updated>2012-06-24T21:27:26Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Avl_u1.jpg&amp;diff=26873</id>
		<title>Файл:Avl u1.jpg</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Avl_u1.jpg&amp;diff=26873"/>
				<updated>2012-06-24T21:25:44Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=25199</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=25199"/>
				<updated>2012-06-12T10:35:13Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Балансировка */&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;
 |  [[Файл:40 03.png|2000x150px]]  &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;
 | [[Файл:40 04.png|2000x150px]]&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; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным 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;
=== Удаление вершины ===&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:Tavltree1.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:Tavltree2.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>Georgy Konoplich</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=25193</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=25193"/>
				<updated>2012-06-12T10:26:52Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Балансировка */&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;
 |  [[Файл:40 03.png|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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;
 | [[Файл:40 04.png|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным 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;
=== Удаление вершины ===&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:Tavltree1.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:Tavltree2.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>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:40_04.png&amp;diff=25188</id>
		<title>Файл:40 04.png</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:40_04.png&amp;diff=25188"/>
				<updated>2012-06-12T10:19:37Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:40_03.png&amp;diff=25178</id>
		<title>Файл:40 03.png</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:40_03.png&amp;diff=25178"/>
				<updated>2012-06-12T09:59:53Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=20212</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=20212"/>
				<updated>2012-04-01T20:32:00Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Слияние двух 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)| \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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным 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;
=== Удаление вершины ===&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;
{|&lt;br /&gt;
| [[File:Tavltree1.jpg|340px|thumb| Дерево &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;
| [[File:Tavltree2.jpg|300px|thumb| Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; после слияния]]&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>Georgy Konoplich</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=20211</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=20211"/>
				<updated>2012-04-01T20:00:51Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Слияние двух 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)| \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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным 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;
=== Удаление вершины ===&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;
[[File:Tavltree1.jpg|300px|thumb|alt=Example alt text| Дерево &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;
[[File:Tavltree2.jpg|300px|thumb|alt=Example alt text| Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;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;
== Литература ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Tavltree2.jpg&amp;diff=20210</id>
		<title>Файл:Tavltree2.jpg</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Tavltree2.jpg&amp;diff=20210"/>
				<updated>2012-04-01T19:52:49Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: загружена новая версия «Файл:Tavltree2.jpg»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Tavltree1.jpg&amp;diff=20209</id>
		<title>Файл:Tavltree1.jpg</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Tavltree1.jpg&amp;diff=20209"/>
				<updated>2012-04-01T19:51:20Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: загружена новая версия «Файл:Tavltree1.jpg»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=20208</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=20208"/>
				<updated>2012-04-01T19:50:39Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Слияние двух 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)| \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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным 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;
=== Удаление вершины ===&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;
[[File:Tavltree1.jpg|thumb|alt=Example alt text| Дерево &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;
[[File:Tavltree2.jpg|thumb|alt=Example alt text| Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;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;
== Литература ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=20207</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=20207"/>
				<updated>2012-04-01T19:46:15Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Слияние двух 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)| \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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным 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;
=== Удаление вершины ===&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;
Дано два дерева &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;
&lt;br /&gt;
[[File:Tavltree1.jpg|thumb|alt=Example alt text| Дерево &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;
В дереве &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;
[[File:Tavltree2.jpg|thumb|alt=Example alt text| Дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;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>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Tavltree1.jpg&amp;diff=20204</id>
		<title>Файл:Tavltree1.jpg</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Tavltree1.jpg&amp;diff=20204"/>
				<updated>2012-04-01T19:28:07Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: загружена новая версия «Файл:Tavltree1.jpg»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Tavltree2.jpg&amp;diff=20203</id>
		<title>Файл:Tavltree2.jpg</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Tavltree2.jpg&amp;diff=20203"/>
				<updated>2012-04-01T19:24:20Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=20202</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=20202"/>
				<updated>2012-04-01T19:23:17Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Слияние двух 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)| \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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным 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;
=== Удаление вершины ===&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;
Дано два дерева &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;
&lt;br /&gt;
[[Файл: Tavltree1.jpg]]&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;
[[Файл: Tavltree2.jpg]]&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>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Tavltree1.jpg&amp;diff=20201</id>
		<title>Файл:Tavltree1.jpg</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Tavltree1.jpg&amp;diff=20201"/>
				<updated>2012-04-01T19:22:07Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Navltree2.jpg&amp;diff=20196</id>
		<title>Файл:Navltree2.jpg</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Navltree2.jpg&amp;diff=20196"/>
				<updated>2012-04-01T16:34:42Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=20195</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=20195"/>
				<updated>2012-04-01T16:32:25Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Слияние двух 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)| \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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным 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;
=== Удаление вершины ===&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;
Дано два дерева &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;
&lt;br /&gt;
[[Файл: Navltree1.jpg]]&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;
[[Файл: Navltree2.jpg]]&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>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Navltree1.jpg&amp;diff=20194</id>
		<title>Файл:Navltree1.jpg</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Navltree1.jpg&amp;diff=20194"/>
				<updated>2012-04-01T16:31:42Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=20193</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=20193"/>
				<updated>2012-04-01T16:22:59Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Высота дерева */&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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным 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;
=== Удаление вершины ===&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;
Дано два дерева &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;
&lt;br /&gt;
[[Файл: Avltree1.jpg]]&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;
[[Файл: Avltree2.jpg]]&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>Georgy Konoplich</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=20156</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=20156"/>
				<updated>2012-03-31T16:25:27Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Высота дерева */&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; {{---}} верно. Тогда &amp;lt;tex&amp;gt;m_{h+1} = F_{h+3} - 1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_{h+1} - m_h = m_h + m_{h-1} + 1 - (m_{h-2} + m_{h-1} + 1) &amp;lt;/tex&amp;gt; (по определению) &amp;lt;tex&amp;gt; = F_{h+3} - 1 - (F_{h+2} - 1) &amp;lt;/tex&amp;gt; (по индукционному переходу) &amp;lt;tex&amp;gt; = F_{h+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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным 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;
=== Удаление вершины ===&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;
Дано два дерева &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;
&lt;br /&gt;
[[Файл: Avltree1.jpg]]&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;
[[Файл: Avltree2.jpg]]&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>Georgy Konoplich</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=20155</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=20155"/>
				<updated>2012-03-31T12:01:20Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Высота дерева */&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; {{---}} верно. Тогда &amp;lt;tex&amp;gt;m_{h+1} = F_{h+3} - 1&amp;lt;/tex&amp;gt;.Так как &amp;lt;tex&amp;gt;m_h = m_{h-1} + m_{h-2} + 1, m_{h+1} = m_h + m_{h-1} + 1&amp;lt;/tex&amp;gt;, то: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_{h+1} - m_h = m_h - m_{h-2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_{h+1} - m_h = F_{h+3} - F_{h+2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_h - m_{h-2} = F_{h+3} - F_{h+2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_{h+2} - F_h = F_{h+1}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_{h+1} = F_{h+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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным 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;
=== Удаление вершины ===&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;
Дано два дерева &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;
&lt;br /&gt;
[[Файл: Avltree1.jpg]]&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;
[[Файл: Avltree2.jpg]]&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>Georgy Konoplich</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=20148</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=20148"/>
				<updated>2012-03-30T17:56:58Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Добавление вершины */&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; {{---}} верно. Тогда &amp;lt;tex&amp;gt;m_{h+1} = F_{h+3} - 1&amp;lt;/tex&amp;gt;.Так как &amp;lt;tex&amp;gt;m_h = m_{h-1} + m_{h-2} + 1, m_{h+1} = m_h + m_{h-1} + 1&amp;lt;/tex&amp;gt;, то: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_{h+1} - m_h = m_h - m_{h-2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_{h+1} - m_h = F_{h+3} - F_{h+2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_h - m_{h-2} = F_{h+3} - F_{h+2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_{h+2} - F_h = F_{h+1}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_{h+1} = F_{h+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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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; увеличивается на единицу, если из правого, то уменьшается на единицу. Если пришли в вершину и её баланс стал равным нулю, то это значит высота поддерева не изменилась и подъём останавливается. Если пришли в вершину и её баланс стал равным 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;
=== Удаление вершины ===&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;
Дано два дерева &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;
&lt;br /&gt;
[[Файл: Avltree1.jpg]]&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;
[[Файл: Avltree2.jpg]]&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>Georgy Konoplich</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=20147</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=20147"/>
				<updated>2012-03-30T17:31:26Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Удаление вершины */&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; {{---}} верно. Тогда &amp;lt;tex&amp;gt;m_{h+1} = F_{h+3} - 1&amp;lt;/tex&amp;gt;.Так как &amp;lt;tex&amp;gt;m_h = m_{h-1} + m_{h-2} + 1, m_{h+1} = m_h + m_{h-1} + 1&amp;lt;/tex&amp;gt;, то: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_{h+1} - m_h = m_h - m_{h-2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_{h+1} - m_h = F_{h+3} - F_{h+2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_h - m_{h-2} = F_{h+3} - F_{h+2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_{h+2} - F_h = F_{h+1}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_{h+1} = F_{h+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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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;diff[i] = 0&amp;lt;/tex&amp;gt;. Если в результате пересчёта баланса, баланс вершины стал равен 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;
=== Удаление вершины ===&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;
Дано два дерева &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;
&lt;br /&gt;
[[Файл: Avltree1.jpg]]&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;
[[Файл: Avltree2.jpg]]&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>Georgy Konoplich</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=20142</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=20142"/>
				<updated>2012-03-30T15:29:45Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Слияние двух 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; {{---}} верно. Тогда &amp;lt;tex&amp;gt;m_{h+1} = F_{h+3} - 1&amp;lt;/tex&amp;gt;.Так как &amp;lt;tex&amp;gt;m_h = m_{h-1} + m_{h-2} + 1, m_{h+1} = m_h + m_{h-1} + 1&amp;lt;/tex&amp;gt;, то: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_{h+1} - m_h = m_h - m_{h-2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_{h+1} - m_h = F_{h+3} - F_{h+2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_h - m_{h-2} = F_{h+3} - F_{h+2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_{h+2} - F_h = F_{h+1}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_{h+1} = F_{h+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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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;diff[i] = 0&amp;lt;/tex&amp;gt;. Если в результате пересчёта баланса, баланс вершины стал равен 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;
=== Удаление вершины ===&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;diff[i] = 0&amp;lt;/tex&amp;gt;. Если в результате пересчёта баланса, баланс вершины стал равен 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;
Дано два дерева &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;
&lt;br /&gt;
[[Файл: Avltree1.jpg]]&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;
[[Файл: Avltree2.jpg]]&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>Georgy Konoplich</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=20141</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=20141"/>
				<updated>2012-03-30T15:24:55Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Высота дерева */&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; {{---}} верно. Тогда &amp;lt;tex&amp;gt;m_{h+1} = F_{h+3} - 1&amp;lt;/tex&amp;gt;.Так как &amp;lt;tex&amp;gt;m_h = m_{h-1} + m_{h-2} + 1, m_{h+1} = m_h + m_{h-1} + 1&amp;lt;/tex&amp;gt;, то: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_{h+1} - m_h = m_h - m_{h-2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_{h+1} - m_h = F_{h+3} - F_{h+2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_h - m_{h-2} = F_{h+3} - F_{h+2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_{h+2} - F_h = F_{h+1}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_{h+1} = F_{h+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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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;diff[i] = 0&amp;lt;/tex&amp;gt;. Если в результате пересчёта баланса, баланс вершины стал равен 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;
=== Удаление вершины ===&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;diff[i] = 0&amp;lt;/tex&amp;gt;. Если в результате пересчёта баланса, баланс вершины стал равен 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;
Дано два дерева &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;
&lt;br /&gt;
[[Файл: Avltree1.jpg]]&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;Q&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;Q&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;Q&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
[[Файл: Avltree2.jpg]]&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>Georgy Konoplich</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=20034</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=20034"/>
				<updated>2012-03-26T20:34:50Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Высота дерева */&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;
Пусть &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;, где &amp;lt;tex&amp;gt;F_h - h&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; {{---}} верно. Тогда &amp;lt;tex&amp;gt;m_{h+1} = F_{h+3} - 1&amp;lt;/tex&amp;gt;.Так как &amp;lt;tex&amp;gt;m_h = m_{h-1} + m_{h-2} + 1, m_{h+1} = m_h + m_{h-1} + 1&amp;lt;/tex&amp;gt;, то: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_{h+1} - m_h = m_h - m_{h-2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_{h+1} - m_h = F_{h+3} - F_{h+2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;m_h - m_{h-2} = F_{h+3} - F_{h+2}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_{h+2} - F_h = F_{h+1}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;F_{h+1} = F_{h+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;
&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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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;diff[i] = 0&amp;lt;/tex&amp;gt;. Если в результате пересчёта баланса, баланс вершины стал равен 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;
=== Удаление вершины ===&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;diff[i] = 0&amp;lt;/tex&amp;gt;. Если в результате пересчёта баланса, баланс вершины стал равен 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;
Дано два дерева &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;
&lt;br /&gt;
[[Файл: Avltree1.jpg]]&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;Q&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;Q&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;Q&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
[[Файл: Avltree2.jpg]]&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>Georgy Konoplich</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=20018</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=20018"/>
				<updated>2012-03-26T18:45:26Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Удаление вершины */&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;
Пусть &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;, где &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;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;. Делая замену &amp;lt;tex&amp;gt;h = \log_\varphi{n}&amp;lt;/tex&amp;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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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;diff[i] = 0&amp;lt;/tex&amp;gt;. Если в результате пересчёта баланса, баланс вершины стал равен 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;
=== Удаление вершины ===&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;diff[i] = 0&amp;lt;/tex&amp;gt;. Если в результате пересчёта баланса, баланс вершины стал равен 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;
Дано два дерева &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;
&lt;br /&gt;
[[Файл: Avltree1.jpg]]&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;Q&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;Q&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;Q&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
[[Файл: Avltree2.jpg]]&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>Georgy Konoplich</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=20015</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=20015"/>
				<updated>2012-03-26T18:42:40Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Удаление вершины */&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;
Пусть &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;, где &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;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;. Делая замену &amp;lt;tex&amp;gt;h = \log_\varphi{n}&amp;lt;/tex&amp;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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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;diff[i] = 0&amp;lt;/tex&amp;gt;. Если в результате пересчёта баланса, баланс вершины стал равен 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;
=== Удаление вершины ===&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;diff[i] = 0&amp;lt;/tex&amp;gt;. Если в результате пересчёта баланса, баланс вершины стал равен 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;
Дано два дерева &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;
&lt;br /&gt;
[[Файл: Avltree1.jpg]]&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;Q&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;Q&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;Q&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
[[Файл: Avltree2.jpg]]&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>Georgy Konoplich</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=20014</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=20014"/>
				<updated>2012-03-26T18:32:41Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Добавление вершины */&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;
Пусть &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;, где &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;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;. Делая замену &amp;lt;tex&amp;gt;h = \log_\varphi{n}&amp;lt;/tex&amp;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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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;diff[i] = 0&amp;lt;/tex&amp;gt;. Если в результате пересчёта баланса, баланс вершины стал равен 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;
=== Удаление вершины ===&lt;br /&gt;
Для простоты опишем рекурсивный алгоритм удаления.&lt;br /&gt;
Если вершина - лист, то [[Дерево поиска, наивная реализация#Удаление|удалим]] её и вызовем балансировку всех её предков в порядке от родителя к корню. Иначе найдём самую близкую по значению вершину и переместим её на место удаляемой вершины, при этом вызвав процедуру её удаления. В процедуре удаления будем идти от переданной вершины к корню и, если пришли из левого поддерева &amp;lt;tex&amp;gt;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Где требуется балансировка, запускаем процедуру балансировки.&lt;br /&gt;
&lt;br /&gt;
В результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по 2-му вызову, нет хотя бы одного из поддеревьев. На балансировку суммарно тратится, как и ранее, &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;
Дано два дерева &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;
&lt;br /&gt;
[[Файл: Avltree1.jpg]]&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;Q&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;Q&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;Q&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
[[Файл: Avltree2.jpg]]&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>Georgy Konoplich</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=20005</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=20005"/>
				<updated>2012-03-26T17:39:23Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Слияние двух 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;
Пусть &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;, где &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;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;. Делая замену &amp;lt;tex&amp;gt;h = \log_\varphi{n}&amp;lt;/tex&amp;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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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;
Процесс включения вершины состоит из двух частей:&lt;br /&gt;
# Прохода по пути поиска, пока не убедимся, что ключа в дереве нет.&lt;br /&gt;
# &amp;quot;Отступления&amp;quot; назад по пути поиска, вплоть до корня и пересчета высот в каждой вершине на обратном пути. При необходимости осуществляется балансировка. Если пришли из левого поддерева &amp;lt;tex&amp;gt;diff[i]&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;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Где требуется балансировка, запускаем процедуру балансировки.&lt;br /&gt;
&lt;br /&gt;
В результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по 2-му вызову, нет хотя бы одного из поддеревьев. На балансировку суммарно тратится, как и ранее, &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;
Дано два дерева &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;
&lt;br /&gt;
[[Файл: Avltree1.jpg]]&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;Q&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;Q&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;Q&amp;lt;/tex&amp;gt; и запускаем балансировку. Таким образом, дерево &amp;lt;tex&amp;gt;T_2&amp;lt;/tex&amp;gt; будет результатом слияния двух АВЛ-деревьев.&lt;br /&gt;
&lt;br /&gt;
[[Файл: Avltree2.jpg]]&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>Georgy Konoplich</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=20004</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=20004"/>
				<updated>2012-03-26T17:38:31Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Слияние двух 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;
Пусть &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;, где &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;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;. Делая замену &amp;lt;tex&amp;gt;h = \log_\varphi{n}&amp;lt;/tex&amp;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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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;
Процесс включения вершины состоит из двух частей:&lt;br /&gt;
# Прохода по пути поиска, пока не убедимся, что ключа в дереве нет.&lt;br /&gt;
# &amp;quot;Отступления&amp;quot; назад по пути поиска, вплоть до корня и пересчета высот в каждой вершине на обратном пути. При необходимости осуществляется балансировка. Если пришли из левого поддерева &amp;lt;tex&amp;gt;diff[i]&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;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Где требуется балансировка, запускаем процедуру балансировки.&lt;br /&gt;
&lt;br /&gt;
В результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по 2-му вызову, нет хотя бы одного из поддеревьев. На балансировку суммарно тратится, как и ранее, &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;
Дано два дерева &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;
&lt;br /&gt;
[[Файл: Avltree1.jpg]]&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;Q&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;Q&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;Q&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;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=20003</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=20003"/>
				<updated>2012-03-26T17:38:01Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Слияние двух 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;
Пусть &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;, где &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;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;. Делая замену &amp;lt;tex&amp;gt;h = \log_\varphi{n}&amp;lt;/tex&amp;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 RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;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 RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;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;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;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 LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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] = -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] = 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] = 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;
В каждом случае операция приводит к нужному результату, а полная высота уменьшается не более чем на 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;
Процесс включения вершины состоит из двух частей:&lt;br /&gt;
# Прохода по пути поиска, пока не убедимся, что ключа в дереве нет.&lt;br /&gt;
# &amp;quot;Отступления&amp;quot; назад по пути поиска, вплоть до корня и пересчета высот в каждой вершине на обратном пути. При необходимости осуществляется балансировка. Если пришли из левого поддерева &amp;lt;tex&amp;gt;diff[i]&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;diff[i]&amp;lt;/tex&amp;gt; увеличивается на единицу, если из правого, то уменьшается на единицу. Где требуется балансировка, запускаем процедуру балансировки.&lt;br /&gt;
&lt;br /&gt;
В результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по 2-му вызову, нет хотя бы одного из поддеревьев. На балансировку суммарно тратится, как и ранее, &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;
Дано два дерева &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;
[[Файл: Avltree1.jpg]]&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;Q&amp;lt;/tex&amp;gt;, корнем &amp;lt;tex&amp;gt;Q&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;Q&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;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Википедия {{---}} АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Avltree2.jpg&amp;diff=20002</id>
		<title>Файл:Avltree2.jpg</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Avltree2.jpg&amp;diff=20002"/>
				<updated>2012-03-26T17:37:16Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Avltree1.jpg&amp;diff=20001</id>
		<title>Файл:Avltree1.jpg</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Avltree1.jpg&amp;diff=20001"/>
				<updated>2012-03-26T17:35:27Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=19772</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=19772"/>
				<updated>2012-03-22T16:24:35Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Удаление вершины */&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;
Высоту поддерева с корнем &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;
Пусть &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;, где &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;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;. Делая замену &amp;lt;tex&amp;gt;h = \log_\varphi{n}&amp;lt;/tex&amp;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;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &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;
&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;
 |  [[Файл:AVL RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение'''&lt;br /&gt;
 | [[Файл:AVL RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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;
# Прохода по пути поиска, пока не убедимся, что ключа в дереве нет.&lt;br /&gt;
# &amp;quot;Отступления&amp;quot; назад по пути поиска, вплоть до корня и пересчета высот в каждой вершине на обратном пути. При необходимости осуществляется балансировка.&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;
Если вершина - лист, то [http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0,_%D0%BD%D0%B0%D0%B8%D0%B2%D0%BD%D0%B0%D1%8F_%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F#.D0.A3.D0.B4.D0.B0.D0.BB.D0.B5.D0.BD.D0.B8.D0.B5 удалим] её и вызовем балансировку всех её предков в порядке от родителя к корню.&lt;br /&gt;
Иначе найдём самую близкую по значению вершину и переместим её на место удаляемой вершины, при этом вызвав процедуру её удаления.&lt;br /&gt;
&lt;br /&gt;
В результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по 2-му вызову, нет хотя бы одного из поддеревьев. На балансировку суммарно тратится, как и ранее, &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;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Wikipedia - АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=19768</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=19768"/>
				<updated>2012-03-22T16:15:16Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Удаление вершины */&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;
Высоту поддерева с корнем &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;
Пусть &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;, где &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;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;. Делая замену &amp;lt;tex&amp;gt;h = \log_\varphi{n}&amp;lt;/tex&amp;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;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &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;
&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;
 |  [[Файл:AVL RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение'''&lt;br /&gt;
 | [[Файл:AVL RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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;
# Прохода по пути поиска, пока не убедимся, что ключа в дереве нет.&lt;br /&gt;
# &amp;quot;Отступления&amp;quot; назад по пути поиска, вплоть до корня и пересчета высот в каждой вершине на обратном пути. При необходимости осуществляется балансировка.&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;
Если вершина - лист, то [[#Удаление|удалим]] её и вызовем балансировку всех её предков в порядке от родителя к корню.&lt;br /&gt;
Иначе найдём самую близкую по значению вершину и переместим её на место удаляемой вершины, при этом вызвав процедуру её удаления.&lt;br /&gt;
&lt;br /&gt;
В результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по 2-му вызову, нет хотя бы одного из поддеревьев. На балансировку суммарно тратится, как и ранее, &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;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Wikipedia - АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=19767</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=19767"/>
				<updated>2012-03-22T16:14:21Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &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;
Высоту поддерева с корнем &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;
Пусть &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;, где &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;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;. Делая замену &amp;lt;tex&amp;gt;h = \log_\varphi{n}&amp;lt;/tex&amp;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;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &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;
&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;
 |  [[Файл:AVL RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение'''&lt;br /&gt;
 | [[Файл:AVL RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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;
# Прохода по пути поиска, пока не убедимся, что ключа в дереве нет.&lt;br /&gt;
# &amp;quot;Отступления&amp;quot; назад по пути поиска, вплоть до корня и пересчета высот в каждой вершине на обратном пути. При необходимости осуществляется балансировка.&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;
Если вершина - лист, то [[Удаление|удалим]] её и вызовем балансировку всех её предков в порядке от родителя к корню.&lt;br /&gt;
Иначе найдём самую близкую по значению вершину и переместим её на место удаляемой вершины, при этом вызвав процедуру её удаления.&lt;br /&gt;
&lt;br /&gt;
В результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по 2-му вызову, нет хотя бы одного из поддеревьев. На балансировку суммарно тратится, как и ранее, &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;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Wikipedia - АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=19766</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=19766"/>
				<updated>2012-03-22T16:05:09Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Высота дерева */&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;
Высоту поддерева с корнем &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;
Пусть &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;, где &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;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;. Делая замену &amp;lt;tex&amp;gt;h = \log_\varphi{n}&amp;lt;/tex&amp;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;
'''Балансировкой вершины''' называется операция, которая в случае разницы высот левого и правого поддеревьев &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;
&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;
 |  [[Файл:AVL RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение'''&lt;br /&gt;
 | [[Файл:AVL RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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;
# Прохода по пути поиска, пока не убедимся, что ключа в дереве нет.&lt;br /&gt;
# &amp;quot;Отступления&amp;quot; назад по пути поиска, вплоть до корня и пересчета высот в каждой вершине на обратном пути. При необходимости осуществляется балансировка.&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;
Если вершина - лист, то удалим её и вызовем балансировку всех её предков в порядке от родителя к корню.&lt;br /&gt;
Иначе найдём самую близкую по значению вершину и переместим её на место удаляемой вершины, при этом вызвав процедуру её удаления.&lt;br /&gt;
&lt;br /&gt;
В результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по 2-му вызову, нет хотя бы одного из поддеревьев. На балансировку суммарно тратится, как и ранее, &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;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Wikipedia - АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=19762</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=19762"/>
				<updated>2012-03-22T15:43:09Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &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;
Высоту поддерева с корнем &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;
Можно показать, что в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt; не менее, чем &amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt; вершин, где &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фиббоначи, &amp;lt;tex&amp;gt;\varphi = \frac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. Делая замену &amp;lt;tex&amp;gt;h = \log_\varphi{n}&amp;lt;/tex&amp;gt;, получаем, что высота AVL-дерева из n вершин - &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;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;
&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;
 |  [[Файл:AVL RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение'''&lt;br /&gt;
 | [[Файл:AVL RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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;
# Прохода по пути поиска, пока не убедимся, что ключа в дереве нет.&lt;br /&gt;
# &amp;quot;Отступления&amp;quot; назад по пути поиска, вплоть до корня и пересчета высот в каждой вершине на обратном пути. При необходимости осуществляется балансировка.&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;
Если вершина - лист, то удалим её и вызовем балансировку всех её предков в порядке от родителя к корню.&lt;br /&gt;
Иначе найдём самую близкую по значению вершину и переместим её на место удаляемой вершины, при этом вызвав процедуру её удаления.&lt;br /&gt;
&lt;br /&gt;
В результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по 2-му вызову, нет хотя бы одного из поддеревьев. На балансировку суммарно тратится, как и ранее, &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;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Wikipedia - АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=19761</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=19761"/>
				<updated>2012-03-22T15:31:14Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Литература */&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;
Высоту поддерева с корнем &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;
Можно показать, что в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt; не менее, чем &amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt; вершин, где &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фиббоначи, &amp;lt;tex&amp;gt;\varphi = \frac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. Делая замену &amp;lt;tex&amp;gt;h = \log_\varphi{n}&amp;lt;/tex&amp;gt;, получаем, что высота AVL-дерева из n вершин - &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;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;
&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;
 |  [[Файл:AVL RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение'''&lt;br /&gt;
 | [[Файл:AVL RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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;
# Прохода по пути поиска, пока не убедимся, что ключа в дереве нет.&lt;br /&gt;
# &amp;quot;Отступления&amp;quot; назад по пути поиска, вплоть до корня и пересчета высот в каждой вершине на обратном пути. При необходимости осуществляется балансировка.&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;
Если вершина - лист, то удалим её и вызовем балансировку всех её предков в порядке от родителя к корню.&lt;br /&gt;
Иначе найдём самую близкую по значению вершину и переместим её на место удаляемой вершины, при этом вызвав процедуру её удаления.&lt;br /&gt;
&lt;br /&gt;
В результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по 2-му вызову, нет хотя бы одного из поддеревьев. На балансировку суммарно тратится, как и ранее, &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;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Wikipedia - АВЛ-дерево]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Деревья поиска]]&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=19760</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=19760"/>
				<updated>2012-03-22T15:28:03Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Литература */&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;
Высоту поддерева с корнем &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;
Можно показать, что в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt; не менее, чем &amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt; вершин, где &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фиббоначи, &amp;lt;tex&amp;gt;\varphi = \frac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. Делая замену &amp;lt;tex&amp;gt;h = \log_\varphi{n}&amp;lt;/tex&amp;gt;, получаем, что высота AVL-дерева из n вершин - &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;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;
&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;
 |  [[Файл:AVL RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение'''&lt;br /&gt;
 | [[Файл:AVL RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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;
# Прохода по пути поиска, пока не убедимся, что ключа в дереве нет.&lt;br /&gt;
# &amp;quot;Отступления&amp;quot; назад по пути поиска, вплоть до корня и пересчета высот в каждой вершине на обратном пути. При необходимости осуществляется балансировка.&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;
Если вершина - лист, то удалим её и вызовем балансировку всех её предков в порядке от родителя к корню.&lt;br /&gt;
Иначе найдём самую близкую по значению вершину и переместим её на место удаляемой вершины, при этом вызвав процедуру её удаления.&lt;br /&gt;
&lt;br /&gt;
В результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по 2-му вызову, нет хотя бы одного из поддеревьев. На балансировку суммарно тратится, как и ранее, &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;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево Wikipedia - АВЛ-дерево]&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</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=19759</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=19759"/>
				<updated>2012-03-22T15:24:35Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &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;
Высоту поддерева с корнем &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;
Можно показать, что в AVL-дереве высоты &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt; не менее, чем &amp;lt;tex&amp;gt;F_h = \Omega(\varphi^h)&amp;lt;/tex&amp;gt; вершин, где &amp;lt;tex&amp;gt;F_h - h&amp;lt;/tex&amp;gt;-ое число Фиббоначи, &amp;lt;tex&amp;gt;\varphi = \frac{ \sqrt{5}+1}{2}&amp;lt;/tex&amp;gt;. Делая замену &amp;lt;tex&amp;gt;h = \log_\varphi{n}&amp;lt;/tex&amp;gt;, получаем, что высота AVL-дерева из n вершин - &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;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;
&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;
 |  [[Файл:AVL RR.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое левое вращение'''&lt;br /&gt;
 | [[Файл:AVL RL.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(L) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(R)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Малое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LL.GIF|2000x150px]]  &lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(C) \le h(L)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Большое правое вращение'''&lt;br /&gt;
 | [[Файл:AVL LR.GIF|2000x150px]]&lt;br /&gt;
 |&amp;lt;tex&amp;gt;h(b) - h(R) = 2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(c) &amp;gt; h(L)&amp;lt;/tex&amp;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;
# Прохода по пути поиска, пока не убедимся, что ключа в дереве нет.&lt;br /&gt;
# &amp;quot;Отступления&amp;quot; назад по пути поиска, вплоть до корня и пересчета высот в каждой вершине на обратном пути. При необходимости осуществляется балансировка.&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;
Если вершина - лист, то удалим её и вызовем балансировку всех её предков в порядке от родителя к корню.&lt;br /&gt;
Иначе найдём самую близкую по значению вершину и переместим её на место удаляемой вершины, при этом вызвав процедуру её удаления.&lt;br /&gt;
&lt;br /&gt;
В результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по 2-му вызову, нет хотя бы одного из поддеревьев. На балансировку суммарно тратится, как и ранее, &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;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/АВЛ-дерево w:АВЛ-дерево]&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D0%BE%D0%BC_%D1%80%D0%B0%D1%81%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B8&amp;diff=13766</id>
		<title>Задача о редакционном расстоянии</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D0%BE%D0%BC_%D1%80%D0%B0%D1%81%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B8&amp;diff=13766"/>
				<updated>2011-12-01T08:27:51Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* Код получения редакторского предписания */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Расстояние Левенштейна''' (также '''редакционное расстояние''' или '''дистанция редактирования''') между двумя строками в теории информации и компьютерной лингвистике — это минимальное количество операций вставки одного символа, удаления одного символа и замены одного символа на другой, необходимых для превращения одной строки в другую.}}&lt;br /&gt;
&lt;br /&gt;
== Свойства ==&lt;br /&gt;
&lt;br /&gt;
Для расстояния Левенштейна справедливы следующие утверждения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) \ge | |S_1| - |S_2| |&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) \le max( |S_1| , |S_2| )&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) = 0 \Leftrightarrow S_1 = S_2&amp;lt;/tex&amp;gt;&lt;br /&gt;
где &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2)&amp;lt;/tex&amp;gt; — расстояние Левенштейна между строками &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, а |S| - длина строки S.&lt;br /&gt;
&lt;br /&gt;
== Редакционное предписание ==&lt;br /&gt;
&lt;br /&gt;
''Редакционным предписанием'' называется последовательность действий, необходимых для получения из первой строки второй кратчайшим образом. Обычно действия обозначаются так: '''D''' (англ. delete) — удалить, '''I''' (англ. insert) — вставить, '''R''' (англ. replace) — заменить, '''M''' (англ. match) — совпадение.&lt;br /&gt;
&lt;br /&gt;
Например, для 2-х строк «hell123» и «hello214» можно построить следующую таблицу преобразований:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border = &amp;quot;1&amp;quot;&lt;br /&gt;
!'''M''' ||'''M''' ||'''M''' ||'''M''' ||'''R''' ||'''M''' ||'''R''' ||'''I'''&lt;br /&gt;
|-&lt;br /&gt;
|'''h''' ||'''e''' ||'''l''' ||'''l''' ||'''1''' ||'''2''' ||'''3''' ||&lt;br /&gt;
|-&lt;br /&gt;
|'''h''' ||'''e''' ||'''l''' ||'''l''' ||'''o''' ||'''2''' ||'''1''' ||'''4'''&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Код получения редакторского предписания ==&lt;br /&gt;
В строке '''S''' содержится последовательность действий, необходимых для получения из первой строки второй кратчайшим образом.&lt;br /&gt;
  readln(s1);&lt;br /&gt;
  readln(s2);&lt;br /&gt;
  n := length(s1);&lt;br /&gt;
  m := length(s2);&lt;br /&gt;
  for i := 1 to n do d[0, i] := i;&lt;br /&gt;
  for i := 1 to m do d[i, 0] := i;&lt;br /&gt;
  for i := 1 to n do for j := 1 to m do&lt;br /&gt;
    if s1[i] = s2[j] then&lt;br /&gt;
    begin&lt;br /&gt;
      d[i, j] := d[i - 1, j - 1];&lt;br /&gt;
      p[i, j].x := i - 1;&lt;br /&gt;
      p[i, j].y := j - 1;&lt;br /&gt;
    end else&lt;br /&gt;
    begin&lt;br /&gt;
      if (d[i - 1, j] &amp;lt;= d[i, j - 1]) and (d[i - 1, j] &amp;lt;= d[i - 1, j - 1]) then&lt;br /&gt;
      begin&lt;br /&gt;
        d[i, j] := d[i - 1, j] +1;&lt;br /&gt;
        p[i, j].x := i - 1;&lt;br /&gt;
        p[i, j].y := j;&lt;br /&gt;
      end;&lt;br /&gt;
      if (d[i, j - 1] &amp;lt;= d[i - 1, j] )and (d[i, j - 1] &amp;lt;= d[i - 1, j - 1]) then&lt;br /&gt;
      begin&lt;br /&gt;
        d[i, j] := d[i, j - 1] +1;&lt;br /&gt;
        p[i, j].x := i;&lt;br /&gt;
        p[i, j].y := j - 1;&lt;br /&gt;
      end;&lt;br /&gt;
      if (d[i - 1, j - 1] &amp;lt;= d[i, j - 1]) and (d[i - 1, j - 1] &amp;lt;= d[i - 1, j]) then&lt;br /&gt;
      begin&lt;br /&gt;
        d[i, j] := d[i - 1, j - 1] + 1;&lt;br /&gt;
        p[i, j].x := i - 1;&lt;br /&gt;
        p[i, j].y := j - 1;&lt;br /&gt;
      end;&lt;br /&gt;
    end;&lt;br /&gt;
  x := n;&lt;br /&gt;
  y := m;&lt;br /&gt;
  while (x &amp;gt; 0) and (y &amp;gt; 0) do&lt;br /&gt;
  begin&lt;br /&gt;
   if p[x, y].x - x = 0 then s := 'D' + s else&lt;br /&gt;
   if p[x, y].y - y = 0 then s := 'I' + s else&lt;br /&gt;
   if s1[x] = s2[y] then s := 'M' + s else s := 'R' + s;&lt;br /&gt;
   x := p[x, y].x;&lt;br /&gt;
   y := p[x, y].y;&lt;br /&gt;
  end;&lt;br /&gt;
  writeln(s);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Разные цены операций ===&lt;br /&gt;
&lt;br /&gt;
Цены операций могут зависеть от вида операции (вставка, удаление, замена) и/или от участвующих в ней символов, отражая разную вероятность разных ошибок при вводе текста, и т. п. В общем случае:&lt;br /&gt;
* w(a, b) — цена замены символа a на символ b&lt;br /&gt;
* w(ε, b) — цена вставки символа b&lt;br /&gt;
* w(a, ε) — цена удаления символа a&lt;br /&gt;
&lt;br /&gt;
Для решения задачи о редакционном расстоянии, необходимо найти последовательность замен, минимизирующую суммарную цену. Расстояние Левенштейна является частным случаем этой задачи при&lt;br /&gt;
* w(a, а) = 0&lt;br /&gt;
* w(a, b) = 1 при a≠b&lt;br /&gt;
* w(ε, b) = 1&lt;br /&gt;
* w(a, ε) = 1&lt;br /&gt;
&lt;br /&gt;
Как частный случай, так и задачу для произвольных w, решает алгоритм Вагнера — Фишера, приведённый ниже. Здесь и ниже мы считаем, что все w неотрицательны, и действует правило треугольника: если две последовательные операции можно заменить одной, это не ухудшает общую цену (например, заменить символ x на y, а потом с y на z не лучше, чем сразу x на z).&lt;br /&gt;
&lt;br /&gt;
== Формула ==&lt;br /&gt;
&lt;br /&gt;
Будем считать, что элементы строк нумеруются с первого, как принято в математике, а не нулевого.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; — две строки (длиной &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;N&amp;lt;/tex&amp;gt; соответственно) над некоторым алфавитом, тогда редакционное расстояние &amp;lt;tex&amp;gt;\rm{d}(S_1, S_2)&amp;lt;/tex&amp;gt; можно подсчитать по следующей рекуррентной формуле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\ \rm{d}(S_1, S_2) = \rm{D}(M,N)&amp;lt;/tex&amp;gt; , где&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\rm{D}(i, j) = \left\{\begin{array}{llcl}&lt;br /&gt;
0&amp;amp;&amp;amp;;&amp;amp;i = 0,\ j = 0\\&lt;br /&gt;
i&amp;amp;&amp;amp;;&amp;amp;j = 0,\ i &amp;gt; 0\\&lt;br /&gt;
j&amp;amp;&amp;amp;;&amp;amp;i = 0,\ j &amp;gt; 0\\&lt;br /&gt;
\rm{min}(\\&lt;br /&gt;
&amp;amp;\rm{D}(i, j - 1) + 1\\&lt;br /&gt;
&amp;amp;\rm{D}(i - 1, j) + 1&amp;amp;;&amp;amp;j &amp;gt; 0,\ i &amp;gt; 0\\&lt;br /&gt;
&amp;amp;\rm{D}(i - 1, j - 1) + \rm{m}(S_1[i], S_2[j])\\&lt;br /&gt;
)&lt;br /&gt;
\end{array}\right.&lt;br /&gt;
&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\rm{m}(a,b)&amp;lt;/tex&amp;gt; равна нулю, если &amp;lt;tex&amp;gt;a = b&amp;lt;/tex&amp;gt; и единице в противном случае; &amp;lt;tex&amp;gt;\min(a, b, c)&amp;lt;/tex&amp;gt; возвращает наименьший из аргументов.&lt;br /&gt;
&lt;br /&gt;
=== Доказательство ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим формулу более подробно. Здесь &amp;lt;tex&amp;gt;D(i, j)&amp;lt;/tex&amp;gt; — расстояние между префиксами строк: первыми i символами строки &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и первыми j символами строки &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;. Очевидно, что редакционное расстояние между двумя пустыми строками равно нулю. Так же очевидно то, что чтобы получить пустую строку из строки длиной &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, нужно совершить &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; операций удаления, а чтобы получить строку длиной &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; из пустой, нужно произвести &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; операций вставки. Осталось рассмотреть нетривиальный случай, когда обе строки непусты.&lt;br /&gt;
&lt;br /&gt;
Для начала заметим, что в оптимальной последовательности операций, их можно произвольно менять местами. В самом деле, рассмотрим две последовательные операции:&lt;br /&gt;
* Две замены одного и того же символа — неоптимально (если мы заменили x на y, потом y на z, выгоднее было сразу заменить x на z).&lt;br /&gt;
* Две замены разных символов можно менять местами&lt;br /&gt;
* Два стирания или две вставки можно менять местами&lt;br /&gt;
* Вставка символа с его последующим стиранием — неоптимально (можно их обе отменить)&lt;br /&gt;
* Стирание и вставку разных символов можно менять местами&lt;br /&gt;
* Вставка символа с его последующей заменой — неоптимально (излишняя замена)&lt;br /&gt;
* Вставка символа и замена другого символа меняются местами&lt;br /&gt;
* Замена символа с его последующим стиранием — неоптимально (излишняя замена)&lt;br /&gt;
* Стирание символа и замена другого символа меняются местами&lt;br /&gt;
&lt;br /&gt;
Пускай &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; кончается на символ «a», &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; кончается на символ «b». Есть три варианта:&lt;br /&gt;
# Символ «а», на который кончается &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt;, в какой-то момент был стёрт. Сделаем это стирание первой операцией. Тогда мы стёрли символ «a», после чего превратили первые &amp;lt;tex&amp;gt;i-1&amp;lt;/tex&amp;gt; символов &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; (на что потребовалось &amp;lt;tex&amp;gt;D(i-1,\ j)&amp;lt;/tex&amp;gt; операций), значит, всего потребовалось &amp;lt;tex&amp;gt;D(i-1,\ j)+1&amp;lt;/tex&amp;gt; операций&lt;br /&gt;
# Символ «b», на который кончается &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, в какой-то момент был добавлен. Сделаем это добавление последней операцией. Мы превратили &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; в первые &amp;lt;tex&amp;gt;j-1&amp;lt;/tex&amp;gt; символов &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, после чего добавили «b». Аналогично предыдущему случаю, потребовалось &amp;lt;tex&amp;gt;D(i,\ j-1)+1&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
# Оба предыдущих утверждения неверны. Если мы добавляли символы справа от финального «a», то чтобы сделать последним символом «b», мы должны были или в какой-то момент добавить его (но тогда утверждение 2 было бы верно), либо заменить на него один из этих добавленных символов (что тоже невозможно, потому что добавление символа с его последующей заменой неоптимально). Значит, символов справа от финального «a» мы не добавляли. Самого финального «a» мы не стирали, поскольку утверждение 1 неверно. Значит, единственный способ изменения последнего символа — его замена. Заменять его 2 или больше раз неоптимально. Значит,&lt;br /&gt;
## Если &amp;lt;tex&amp;gt;a=b&amp;lt;/tex&amp;gt;, мы последний символ не меняли. Поскольку мы его также не стирали и не приписывали ничего справа от него, он не влиял на наши действия, и, значит, мы выполнили &amp;lt;tex&amp;gt;D(i-1,\ j-1)&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
## Если &amp;lt;tex&amp;gt;a\ne b&amp;lt;/tex&amp;gt;, мы последний символ меняли один раз. Сделаем эту замену первой. В дальнейшем, аналогично предыдущему случаю, мы должны выполнить &amp;lt;tex&amp;gt;D(i-1,\ j-1)&amp;lt;/tex&amp;gt; операций, значит, всего потребуется &amp;lt;tex&amp;gt;D(i-1,\ j-1)+1&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Вагнера — Фишера ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения кратчайшего расстояния необходимо вычислить матрицу D, используя [[#Формула|вышеприведённую формулу]]. Её можно вычислять как по строкам, так и по столбцам. &lt;br /&gt;
Псевдокод алгоритма, написанный при произвольных ценах замен, вставок и удалений (важно помнить, что элементы нумеруются с 1):&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 D(0,0) = 0&lt;br /&gt;
 для всех j от 1 до N&lt;br /&gt;
   D(0,j) = D(0,j-1) + цена вставки символа S2[j]&lt;br /&gt;
 для всех i от 1 до M&lt;br /&gt;
   D(i,0) = D(i-1,0) + цена удаления символа S1[i]&lt;br /&gt;
   для всех j от 1 до N&lt;br /&gt;
     D(i,j) = min(&lt;br /&gt;
        D(i-1, j) + цена удаления символа S1[i],&lt;br /&gt;
        D(i, j-1) + цена вставки символа S2[j],&lt;br /&gt;
        D(i-1, j-1) + цена замены символа S1[i] на символ S2[j]&lt;br /&gt;
     )&lt;br /&gt;
 вернуть D(M,N)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Память ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм в виде, описанном выше, требует &amp;lt;tex&amp;gt;\Theta(M \cdot N)&amp;lt;/tex&amp;gt; операций и такую же память, однако, если требуется только расстояние, легко уменьшить требуемую память до &amp;lt;tex&amp;gt;\Theta(\min(M,N))&amp;lt;/tex&amp;gt;. Для этого надо учесть, что после вычисления любой строки предыдущая строка больше не нужна. Более того, после вычисления D(i, j) не нужны также D(i-1,0) … D(i-1,j-1). Поэтому алгоритм можно переписать как&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 для всех i от 0 до M&lt;br /&gt;
   для всех j от 0 до N&lt;br /&gt;
     вычислить D(i, j)&lt;br /&gt;
     если i&amp;gt;0 и j&amp;gt;0&lt;br /&gt;
       стереть D(i-1, j-1)&lt;br /&gt;
 вернуть D(M, N)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
*http://en.wikipedia.org&lt;br /&gt;
*Романовский И.В. &amp;quot;Дискретный анализ&amp;quot;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D0%BE%D0%BC_%D1%80%D0%B0%D1%81%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B8&amp;diff=13666</id>
		<title>Задача о редакционном расстоянии</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D0%BE%D0%BC_%D1%80%D0%B0%D1%81%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B8&amp;diff=13666"/>
				<updated>2011-11-30T01:09:06Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Расстояние Левенштейна''' (также '''редакционное расстояние''' или '''дистанция редактирования''') между двумя строками в теории информации и компьютерной лингвистике — это минимальное количество операций вставки одного символа, удаления одного символа и замены одного символа на другой, необходимых для превращения одной строки в другую.}}&lt;br /&gt;
&lt;br /&gt;
== Свойства ==&lt;br /&gt;
&lt;br /&gt;
Для расстояния Левенштейна справедливы следующие утверждения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) \ge | |S_1| - |S_2| |&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) \le max( |S_1| , |S_2| )&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) = 0 \Leftrightarrow S_1 = S_2&amp;lt;/tex&amp;gt;&lt;br /&gt;
где &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2)&amp;lt;/tex&amp;gt; — расстояние Левенштейна между строками &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, а |S| - длина строки S.&lt;br /&gt;
&lt;br /&gt;
== Редакционное предписание ==&lt;br /&gt;
&lt;br /&gt;
''Редакционным предписанием'' называется последовательность действий, необходимых для получения из первой строки второй кратчайшим образом. Обычно действия обозначаются так: '''D''' (англ. delete) — удалить, '''I''' (англ. insert) — вставить, '''R''' (англ. replace) — заменить, '''M''' (англ. match) — совпадение.&lt;br /&gt;
&lt;br /&gt;
Например, для 2-х строк «hell123» и «hello214» можно построить следующую таблицу преобразований:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border = &amp;quot;1&amp;quot;&lt;br /&gt;
!'''M''' ||'''M''' ||'''M''' ||'''M''' ||'''R''' ||'''M''' ||'''R''' ||'''I'''&lt;br /&gt;
|-&lt;br /&gt;
|'''h''' ||'''e''' ||'''l''' ||'''l''' ||'''1''' ||'''2''' ||'''3''' ||&lt;br /&gt;
|-&lt;br /&gt;
|'''h''' ||'''e''' ||'''l''' ||'''l''' ||'''o''' ||'''2''' ||'''1''' ||'''4'''&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Код получения редакторского предписания ==&lt;br /&gt;
В строке '''S''' содержится последовательность действий, необходимых для получения из первой строки второй кратчайшим образом.&lt;br /&gt;
  readln(s1);&lt;br /&gt;
  readln(s2);&lt;br /&gt;
  n := length(s1);&lt;br /&gt;
  m := length(s2);&lt;br /&gt;
  for i := 1 to n do d[0, i] := i;&lt;br /&gt;
  for i := 1 to m do d[i, 0] := i;&lt;br /&gt;
  for i := 1 to n do for j := 1 to m do&lt;br /&gt;
    if s1[i] = s2[j] then&lt;br /&gt;
    begin&lt;br /&gt;
      d[i, j] := d[i - 1, j - 1];&lt;br /&gt;
      p[i, j].x := i - 1;&lt;br /&gt;
      p[i, j].y := j - 1;&lt;br /&gt;
    end else&lt;br /&gt;
    begin&lt;br /&gt;
      if (d[i - 1, j] &amp;lt;= d[i, j - 1])and(d[i - 1, j] &amp;lt;= d[i - 1, j - 1]) then&lt;br /&gt;
      begin&lt;br /&gt;
        d[i, j] := d[i - 1, j] +1;&lt;br /&gt;
        p[i, j].x := i - 1;&lt;br /&gt;
        p[i, j].y := j;&lt;br /&gt;
      end;&lt;br /&gt;
      if (d[i, j - 1] &amp;lt;= d[i - 1, j])and(d[i, j - 1] &amp;lt;= d[i - 1, j - 1]) then&lt;br /&gt;
      begin&lt;br /&gt;
        d[i, j] := d[i, j - 1] +1;&lt;br /&gt;
        p[i, j].x := i;&lt;br /&gt;
        p[i, j].y := j - 1;&lt;br /&gt;
      end;&lt;br /&gt;
      if (d[i - 1, j - 1] &amp;lt;= d[i, j - 1])and(d[i - 1, j - 1] &amp;lt;= d[i - 1, j]) then&lt;br /&gt;
      begin&lt;br /&gt;
        d[i, j] := d[i - 1, j - 1] + 1;&lt;br /&gt;
        p[i, j].x := i - 1;&lt;br /&gt;
        p[i, j].y := j - 1;&lt;br /&gt;
      end;&lt;br /&gt;
    end;&lt;br /&gt;
  x := n;&lt;br /&gt;
  y := m;&lt;br /&gt;
  while (x &amp;gt; 0) and (y &amp;gt; 0) do&lt;br /&gt;
  begin&lt;br /&gt;
   if p[x, y].x - x = 0 then s := 'D' + s else&lt;br /&gt;
   if p[x, y].y - y = 0 then s := 'I' + s else&lt;br /&gt;
   if s1[x] = s2[y] then s := 'M' + s else s := 'R' + s;&lt;br /&gt;
   x := p[x, y].x;&lt;br /&gt;
   y := p[x, y].y;&lt;br /&gt;
  end;&lt;br /&gt;
  writeln(s);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Разные цены операций ===&lt;br /&gt;
&lt;br /&gt;
Цены операций могут зависеть от вида операции (вставка, удаление, замена) и/или от участвующих в ней символов, отражая разную вероятность разных ошибок при вводе текста, и т. п. В общем случае:&lt;br /&gt;
* w(a, b) — цена замены символа a на символ b&lt;br /&gt;
* w(ε, b) — цена вставки символа b&lt;br /&gt;
* w(a, ε) — цена удаления символа a&lt;br /&gt;
&lt;br /&gt;
Для решения задачи о редакционном расстоянии, необходимо найти последовательность замен, минимизирующую суммарную цену. Расстояние Левенштейна является частным случаем этой задачи при&lt;br /&gt;
* w(a, а) = 0&lt;br /&gt;
* w(a, b) = 1 при a≠b&lt;br /&gt;
* w(ε, b) = 1&lt;br /&gt;
* w(a, ε) = 1&lt;br /&gt;
&lt;br /&gt;
Как частный случай, так и задачу для произвольных w, решает алгоритм Вагнера — Фишера, приведённый ниже. Здесь и ниже мы считаем, что все w неотрицательны, и действует правило треугольника: если две последовательные операции можно заменить одной, это не ухудшает общую цену (например, заменить символ x на y, а потом с y на z не лучше, чем сразу x на z).&lt;br /&gt;
&lt;br /&gt;
== Формула ==&lt;br /&gt;
&lt;br /&gt;
Будем считать, что элементы строк нумеруются с первого, как принято в математике, а не нулевого.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; — две строки (длиной &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;N&amp;lt;/tex&amp;gt; соответственно) над некоторым алфавитом, тогда редакционное расстояние &amp;lt;tex&amp;gt;\rm{d}(S_1, S_2)&amp;lt;/tex&amp;gt; можно подсчитать по следующей рекуррентной формуле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\ \rm{d}(S_1, S_2) = \rm{D}(M,N)&amp;lt;/tex&amp;gt; , где&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\rm{D}(i, j) = \left\{\begin{array}{llcl}&lt;br /&gt;
0&amp;amp;&amp;amp;;&amp;amp;i = 0,\ j = 0\\&lt;br /&gt;
i&amp;amp;&amp;amp;;&amp;amp;j = 0,\ i &amp;gt; 0\\&lt;br /&gt;
j&amp;amp;&amp;amp;;&amp;amp;i = 0,\ j &amp;gt; 0\\&lt;br /&gt;
\rm{min}(\\&lt;br /&gt;
&amp;amp;\rm{D}(i, j - 1) + 1\\&lt;br /&gt;
&amp;amp;\rm{D}(i - 1, j) + 1&amp;amp;;&amp;amp;j &amp;gt; 0,\ i &amp;gt; 0\\&lt;br /&gt;
&amp;amp;\rm{D}(i - 1, j - 1) + \rm{m}(S_1[i], S_2[j])\\&lt;br /&gt;
)&lt;br /&gt;
\end{array}\right.&lt;br /&gt;
&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\rm{m}(a,b)&amp;lt;/tex&amp;gt; равна нулю, если &amp;lt;tex&amp;gt;a = b&amp;lt;/tex&amp;gt; и единице в противном случае; &amp;lt;tex&amp;gt;\min(a, b, c)&amp;lt;/tex&amp;gt; возвращает наименьший из аргументов.&lt;br /&gt;
&lt;br /&gt;
=== Доказательство ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим формулу более подробно. Здесь &amp;lt;tex&amp;gt;D(i, j)&amp;lt;/tex&amp;gt; — расстояние между префиксами строк: первыми i символами строки &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и первыми j символами строки &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;. Очевидно, что редакционное расстояние между двумя пустыми строками равно нулю. Так же очевидно то, что чтобы получить пустую строку из строки длиной &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, нужно совершить &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; операций удаления, а чтобы получить строку длиной &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; из пустой, нужно произвести &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; операций вставки. Осталось рассмотреть нетривиальный случай, когда обе строки непусты.&lt;br /&gt;
&lt;br /&gt;
Для начала заметим, что в оптимальной последовательности операций, их можно произвольно менять местами. В самом деле, рассмотрим две последовательные операции:&lt;br /&gt;
* Две замены одного и того же символа — неоптимально (если мы заменили x на y, потом y на z, выгоднее было сразу заменить x на z).&lt;br /&gt;
* Две замены разных символов можно менять местами&lt;br /&gt;
* Два стирания или две вставки можно менять местами&lt;br /&gt;
* Вставка символа с его последующим стиранием — неоптимально (можно их обе отменить)&lt;br /&gt;
* Стирание и вставку разных символов можно менять местами&lt;br /&gt;
* Вставка символа с его последующей заменой — неоптимально (излишняя замена)&lt;br /&gt;
* Вставка символа и замена другого символа меняются местами&lt;br /&gt;
* Замена символа с его последующим стиранием — неоптимально (излишняя замена)&lt;br /&gt;
* Стирание символа и замена другого символа меняются местами&lt;br /&gt;
&lt;br /&gt;
Пускай &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; кончается на символ «a», &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; кончается на символ «b». Есть три варианта:&lt;br /&gt;
# Символ «а», на который кончается &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt;, в какой-то момент был стёрт. Сделаем это стирание первой операцией. Тогда мы стёрли символ «a», после чего превратили первые &amp;lt;tex&amp;gt;i-1&amp;lt;/tex&amp;gt; символов &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; (на что потребовалось &amp;lt;tex&amp;gt;D(i-1,\ j)&amp;lt;/tex&amp;gt; операций), значит, всего потребовалось &amp;lt;tex&amp;gt;D(i-1,\ j)+1&amp;lt;/tex&amp;gt; операций&lt;br /&gt;
# Символ «b», на который кончается &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, в какой-то момент был добавлен. Сделаем это добавление последней операцией. Мы превратили &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; в первые &amp;lt;tex&amp;gt;j-1&amp;lt;/tex&amp;gt; символов &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, после чего добавили «b». Аналогично предыдущему случаю, потребовалось &amp;lt;tex&amp;gt;D(i,\ j-1)+1&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
# Оба предыдущих утверждения неверны. Если мы добавляли символы справа от финального «a», то чтобы сделать последним символом «b», мы должны были или в какой-то момент добавить его (но тогда утверждение 2 было бы верно), либо заменить на него один из этих добавленных символов (что тоже невозможно, потому что добавление символа с его последующей заменой неоптимально). Значит, символов справа от финального «a» мы не добавляли. Самого финального «a» мы не стирали, поскольку утверждение 1 неверно. Значит, единственный способ изменения последнего символа — его замена. Заменять его 2 или больше раз неоптимально. Значит,&lt;br /&gt;
## Если &amp;lt;tex&amp;gt;a=b&amp;lt;/tex&amp;gt;, мы последний символ не меняли. Поскольку мы его также не стирали и не приписывали ничего справа от него, он не влиял на наши действия, и, значит, мы выполнили &amp;lt;tex&amp;gt;D(i-1,\ j-1)&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
## Если &amp;lt;tex&amp;gt;a\ne b&amp;lt;/tex&amp;gt;, мы последний символ меняли один раз. Сделаем эту замену первой. В дальнейшем, аналогично предыдущему случаю, мы должны выполнить &amp;lt;tex&amp;gt;D(i-1,\ j-1)&amp;lt;/tex&amp;gt; операций, значит, всего потребуется &amp;lt;tex&amp;gt;D(i-1,\ j-1)+1&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Вагнера — Фишера ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения кратчайшего расстояния необходимо вычислить матрицу D, используя [[#Формула|вышеприведённую формулу]]. Её можно вычислять как по строкам, так и по столбцам. &lt;br /&gt;
Псевдокод алгоритма, написанный при произвольных ценах замен, вставок и удалений (важно помнить, что элементы нумеруются с 1):&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 D(0,0) = 0&lt;br /&gt;
 для всех j от 1 до N&lt;br /&gt;
   D(0,j) = D(0,j-1) + цена вставки символа S2[j]&lt;br /&gt;
 для всех i от 1 до M&lt;br /&gt;
   D(i,0) = D(i-1,0) + цена удаления символа S1[i]&lt;br /&gt;
   для всех j от 1 до N&lt;br /&gt;
     D(i,j) = min(&lt;br /&gt;
        D(i-1, j) + цена удаления символа S1[i],&lt;br /&gt;
        D(i, j-1) + цена вставки символа S2[j],&lt;br /&gt;
        D(i-1, j-1) + цена замены символа S1[i] на символ S2[j]&lt;br /&gt;
     )&lt;br /&gt;
 вернуть D(M,N)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Память ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм в виде, описанном выше, требует &amp;lt;tex&amp;gt;\Theta(M \cdot N)&amp;lt;/tex&amp;gt; операций и такую же память, однако, если требуется только расстояние, легко уменьшить требуемую память до &amp;lt;tex&amp;gt;\Theta(\min(M,N))&amp;lt;/tex&amp;gt;. Для этого надо учесть, что после вычисления любой строки предыдущая строка больше не нужна. Более того, после вычисления D(i, j) не нужны также D(i-1,0) … D(i-1,j-1). Поэтому алгоритм можно переписать как&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 для всех i от 0 до M&lt;br /&gt;
   для всех j от 0 до N&lt;br /&gt;
     вычислить D(i, j)&lt;br /&gt;
     если i&amp;gt;0 и j&amp;gt;0&lt;br /&gt;
       стереть D(i-1, j-1)&lt;br /&gt;
 вернуть D(M, N)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
*http://en.wikipedia.org&lt;br /&gt;
*Романовский И.В. &amp;quot;Дискретный анализ&amp;quot;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D0%BE%D0%BC_%D1%80%D0%B0%D1%81%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B8&amp;diff=13665</id>
		<title>Задача о редакционном расстоянии</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D0%BE%D0%BC_%D1%80%D0%B0%D1%81%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B8&amp;diff=13665"/>
				<updated>2011-11-30T01:06:49Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: /* код получения редакторского предписания */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Расстояние Левенштейна''' (также '''редакционное расстояние''' или '''дистанция редактирования''') между двумя строками в теории информации и компьютерной лингвистике — это минимальное количество операций вставки одного символа, удаления одного символа и замены одного символа на другой, необходимых для превращения одной строки в другую.}}&lt;br /&gt;
&lt;br /&gt;
== Свойства ==&lt;br /&gt;
&lt;br /&gt;
Для расстояния Левенштейна справедливы следующие утверждения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) \ge | |S_1| - |S_2| |&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) \le max( |S_1| , |S_2| )&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) = 0 \Leftrightarrow S_1 = S_2&amp;lt;/tex&amp;gt;&lt;br /&gt;
где &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2)&amp;lt;/tex&amp;gt; — расстояние Левенштейна между строками &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, а |S| - длина строки S.&lt;br /&gt;
&lt;br /&gt;
== Редакционное предписание ==&lt;br /&gt;
&lt;br /&gt;
''Редакционным предписанием'' называется последовательность действий, необходимых для получения из первой строки второй кратчайшим образом. Обычно действия обозначаются так: '''D''' (англ. delete) — удалить, '''I''' (англ. insert) — вставить, '''R''' (англ. replace) — заменить, '''M''' (англ. match) — совпадение.&lt;br /&gt;
&lt;br /&gt;
Например, для 2-х строк «hell123» и «hello214» можно построить следующую таблицу преобразований:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border = &amp;quot;1&amp;quot;&lt;br /&gt;
!'''M''' ||'''M''' ||'''M''' ||'''M''' ||'''R''' ||'''M''' ||'''R''' ||'''I'''&lt;br /&gt;
|-&lt;br /&gt;
|'''h''' ||'''e''' ||'''l''' ||'''l''' ||'''1''' ||'''2''' ||'''3''' ||&lt;br /&gt;
|-&lt;br /&gt;
|'''h''' ||'''e''' ||'''l''' ||'''l''' ||'''o''' ||'''2''' ||'''1''' ||'''4'''&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Код получения редакторского предписания ==&lt;br /&gt;
&lt;br /&gt;
  readln(s1);&lt;br /&gt;
  readln(s2);&lt;br /&gt;
  n := length(s1);&lt;br /&gt;
  m := length(s2);&lt;br /&gt;
  for i := 1 to n do d[0, i] := i;&lt;br /&gt;
  for i := 1 to m do d[i, 0] := i;&lt;br /&gt;
  for i := 1 to n do for j := 1 to m do&lt;br /&gt;
    if s1[i] = s2[j] then&lt;br /&gt;
    begin&lt;br /&gt;
      d[i, j] := d[i - 1, j - 1];&lt;br /&gt;
      p[i, j].x := i - 1;&lt;br /&gt;
      p[i, j].y := j - 1;&lt;br /&gt;
    end else&lt;br /&gt;
    begin&lt;br /&gt;
      if (d[i - 1, j] &amp;lt;= d[i, j - 1])and(d[i - 1, j] &amp;lt;= d[i - 1, j - 1]) then&lt;br /&gt;
      begin&lt;br /&gt;
        d[i, j] := d[i - 1, j] +1;&lt;br /&gt;
        p[i, j].x := i - 1;&lt;br /&gt;
        p[i, j].y := j;&lt;br /&gt;
      end;&lt;br /&gt;
      if (d[i, j - 1] &amp;lt;= d[i - 1, j])and(d[i, j - 1] &amp;lt;= d[i - 1, j - 1]) then&lt;br /&gt;
      begin&lt;br /&gt;
        d[i, j] := d[i, j - 1] +1;&lt;br /&gt;
        p[i, j].x := i;&lt;br /&gt;
        p[i, j].y := j - 1;&lt;br /&gt;
      end;&lt;br /&gt;
      if (d[i - 1, j - 1] &amp;lt;= d[i, j - 1])and(d[i - 1, j - 1] &amp;lt;= d[i - 1, j]) then&lt;br /&gt;
      begin&lt;br /&gt;
        d[i, j] := d[i - 1, j - 1] + 1;&lt;br /&gt;
        p[i, j].x := i - 1;&lt;br /&gt;
        p[i, j].y := j - 1;&lt;br /&gt;
      end;&lt;br /&gt;
    end;&lt;br /&gt;
  x := n;&lt;br /&gt;
  y := m;&lt;br /&gt;
  while (x &amp;gt; 0) and (y &amp;gt; 0) do&lt;br /&gt;
  begin&lt;br /&gt;
   if p[x, y].x - x = 0 then s := 'D' + s else&lt;br /&gt;
   if p[x, y].y - y = 0 then s := 'I' + s else&lt;br /&gt;
   if s1[x] = s2[y] then s := 'M' + s else s := 'R' + s;&lt;br /&gt;
   x := p[x, y].x;&lt;br /&gt;
   y := p[x, y].y;&lt;br /&gt;
  end;&lt;br /&gt;
  writeln(s);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Разные цены операций ===&lt;br /&gt;
&lt;br /&gt;
Цены операций могут зависеть от вида операции (вставка, удаление, замена) и/или от участвующих в ней символов, отражая разную вероятность разных ошибок при вводе текста, и т. п. В общем случае:&lt;br /&gt;
* w(a, b) — цена замены символа a на символ b&lt;br /&gt;
* w(ε, b) — цена вставки символа b&lt;br /&gt;
* w(a, ε) — цена удаления символа a&lt;br /&gt;
&lt;br /&gt;
Для решения задачи о редакционном расстоянии, необходимо найти последовательность замен, минимизирующую суммарную цену. Расстояние Левенштейна является частным случаем этой задачи при&lt;br /&gt;
* w(a, а) = 0&lt;br /&gt;
* w(a, b) = 1 при a≠b&lt;br /&gt;
* w(ε, b) = 1&lt;br /&gt;
* w(a, ε) = 1&lt;br /&gt;
&lt;br /&gt;
Как частный случай, так и задачу для произвольных w, решает алгоритм Вагнера — Фишера, приведённый ниже. Здесь и ниже мы считаем, что все w неотрицательны, и действует правило треугольника: если две последовательные операции можно заменить одной, это не ухудшает общую цену (например, заменить символ x на y, а потом с y на z не лучше, чем сразу x на z).&lt;br /&gt;
&lt;br /&gt;
== Формула ==&lt;br /&gt;
&lt;br /&gt;
Будем считать, что элементы строк нумеруются с первого, как принято в математике, а не нулевого.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; — две строки (длиной &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;N&amp;lt;/tex&amp;gt; соответственно) над некоторым алфавитом, тогда редакционное расстояние &amp;lt;tex&amp;gt;\rm{d}(S_1, S_2)&amp;lt;/tex&amp;gt; можно подсчитать по следующей рекуррентной формуле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\ \rm{d}(S_1, S_2) = \rm{D}(M,N)&amp;lt;/tex&amp;gt; , где&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\rm{D}(i, j) = \left\{\begin{array}{llcl}&lt;br /&gt;
0&amp;amp;&amp;amp;;&amp;amp;i = 0,\ j = 0\\&lt;br /&gt;
i&amp;amp;&amp;amp;;&amp;amp;j = 0,\ i &amp;gt; 0\\&lt;br /&gt;
j&amp;amp;&amp;amp;;&amp;amp;i = 0,\ j &amp;gt; 0\\&lt;br /&gt;
\rm{min}(\\&lt;br /&gt;
&amp;amp;\rm{D}(i, j - 1) + 1\\&lt;br /&gt;
&amp;amp;\rm{D}(i - 1, j) + 1&amp;amp;;&amp;amp;j &amp;gt; 0,\ i &amp;gt; 0\\&lt;br /&gt;
&amp;amp;\rm{D}(i - 1, j - 1) + \rm{m}(S_1[i], S_2[j])\\&lt;br /&gt;
)&lt;br /&gt;
\end{array}\right.&lt;br /&gt;
&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\rm{m}(a,b)&amp;lt;/tex&amp;gt; равна нулю, если &amp;lt;tex&amp;gt;a = b&amp;lt;/tex&amp;gt; и единице в противном случае; &amp;lt;tex&amp;gt;\min(a, b, c)&amp;lt;/tex&amp;gt; возвращает наименьший из аргументов.&lt;br /&gt;
&lt;br /&gt;
=== Доказательство ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим формулу более подробно. Здесь &amp;lt;tex&amp;gt;D(i, j)&amp;lt;/tex&amp;gt; — расстояние между префиксами строк: первыми i символами строки &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и первыми j символами строки &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;. Очевидно, что редакционное расстояние между двумя пустыми строками равно нулю. Так же очевидно то, что чтобы получить пустую строку из строки длиной &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, нужно совершить &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; операций удаления, а чтобы получить строку длиной &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; из пустой, нужно произвести &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; операций вставки. Осталось рассмотреть нетривиальный случай, когда обе строки непусты.&lt;br /&gt;
&lt;br /&gt;
Для начала заметим, что в оптимальной последовательности операций, их можно произвольно менять местами. В самом деле, рассмотрим две последовательные операции:&lt;br /&gt;
* Две замены одного и того же символа — неоптимально (если мы заменили x на y, потом y на z, выгоднее было сразу заменить x на z).&lt;br /&gt;
* Две замены разных символов можно менять местами&lt;br /&gt;
* Два стирания или две вставки можно менять местами&lt;br /&gt;
* Вставка символа с его последующим стиранием — неоптимально (можно их обе отменить)&lt;br /&gt;
* Стирание и вставку разных символов можно менять местами&lt;br /&gt;
* Вставка символа с его последующей заменой — неоптимально (излишняя замена)&lt;br /&gt;
* Вставка символа и замена другого символа меняются местами&lt;br /&gt;
* Замена символа с его последующим стиранием — неоптимально (излишняя замена)&lt;br /&gt;
* Стирание символа и замена другого символа меняются местами&lt;br /&gt;
&lt;br /&gt;
Пускай &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; кончается на символ «a», &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; кончается на символ «b». Есть три варианта:&lt;br /&gt;
# Символ «а», на который кончается &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt;, в какой-то момент был стёрт. Сделаем это стирание первой операцией. Тогда мы стёрли символ «a», после чего превратили первые &amp;lt;tex&amp;gt;i-1&amp;lt;/tex&amp;gt; символов &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; (на что потребовалось &amp;lt;tex&amp;gt;D(i-1,\ j)&amp;lt;/tex&amp;gt; операций), значит, всего потребовалось &amp;lt;tex&amp;gt;D(i-1,\ j)+1&amp;lt;/tex&amp;gt; операций&lt;br /&gt;
# Символ «b», на который кончается &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, в какой-то момент был добавлен. Сделаем это добавление последней операцией. Мы превратили &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; в первые &amp;lt;tex&amp;gt;j-1&amp;lt;/tex&amp;gt; символов &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, после чего добавили «b». Аналогично предыдущему случаю, потребовалось &amp;lt;tex&amp;gt;D(i,\ j-1)+1&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
# Оба предыдущих утверждения неверны. Если мы добавляли символы справа от финального «a», то чтобы сделать последним символом «b», мы должны были или в какой-то момент добавить его (но тогда утверждение 2 было бы верно), либо заменить на него один из этих добавленных символов (что тоже невозможно, потому что добавление символа с его последующей заменой неоптимально). Значит, символов справа от финального «a» мы не добавляли. Самого финального «a» мы не стирали, поскольку утверждение 1 неверно. Значит, единственный способ изменения последнего символа — его замена. Заменять его 2 или больше раз неоптимально. Значит,&lt;br /&gt;
## Если &amp;lt;tex&amp;gt;a=b&amp;lt;/tex&amp;gt;, мы последний символ не меняли. Поскольку мы его также не стирали и не приписывали ничего справа от него, он не влиял на наши действия, и, значит, мы выполнили &amp;lt;tex&amp;gt;D(i-1,\ j-1)&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
## Если &amp;lt;tex&amp;gt;a\ne b&amp;lt;/tex&amp;gt;, мы последний символ меняли один раз. Сделаем эту замену первой. В дальнейшем, аналогично предыдущему случаю, мы должны выполнить &amp;lt;tex&amp;gt;D(i-1,\ j-1)&amp;lt;/tex&amp;gt; операций, значит, всего потребуется &amp;lt;tex&amp;gt;D(i-1,\ j-1)+1&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Вагнера — Фишера ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения кратчайшего расстояния необходимо вычислить матрицу D, используя [[#Формула|вышеприведённую формулу]]. Её можно вычислять как по строкам, так и по столбцам. &lt;br /&gt;
Псевдокод алгоритма, написанный при произвольных ценах замен, вставок и удалений (важно помнить, что элементы нумеруются с 1):&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 D(0,0) = 0&lt;br /&gt;
 для всех j от 1 до N&lt;br /&gt;
   D(0,j) = D(0,j-1) + цена вставки символа S2[j]&lt;br /&gt;
 для всех i от 1 до M&lt;br /&gt;
   D(i,0) = D(i-1,0) + цена удаления символа S1[i]&lt;br /&gt;
   для всех j от 1 до N&lt;br /&gt;
     D(i,j) = min(&lt;br /&gt;
        D(i-1, j) + цена удаления символа S1[i],&lt;br /&gt;
        D(i, j-1) + цена вставки символа S2[j],&lt;br /&gt;
        D(i-1, j-1) + цена замены символа S1[i] на символ S2[j]&lt;br /&gt;
     )&lt;br /&gt;
 вернуть D(M,N)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Память ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм в виде, описанном выше, требует &amp;lt;tex&amp;gt;\Theta(M \cdot N)&amp;lt;/tex&amp;gt; операций и такую же память, однако, если требуется только расстояние, легко уменьшить требуемую память до &amp;lt;tex&amp;gt;\Theta(\min(M,N))&amp;lt;/tex&amp;gt;. Для этого надо учесть, что после вычисления любой строки предыдущая строка больше не нужна. Более того, после вычисления D(i, j) не нужны также D(i-1,0) … D(i-1,j-1). Поэтому алгоритм можно переписать как&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 для всех i от 0 до M&lt;br /&gt;
   для всех j от 0 до N&lt;br /&gt;
     вычислить D(i, j)&lt;br /&gt;
     если i&amp;gt;0 и j&amp;gt;0&lt;br /&gt;
       стереть D(i-1, j-1)&lt;br /&gt;
 вернуть D(M, N)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
*http://en.wikipedia.org&lt;br /&gt;
*Романовский И.В. &amp;quot;Дискретный анализ&amp;quot;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D0%BE%D0%BC_%D1%80%D0%B0%D1%81%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B8&amp;diff=13664</id>
		<title>Задача о редакционном расстоянии</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D0%BE%D0%BC_%D1%80%D0%B0%D1%81%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B8&amp;diff=13664"/>
				<updated>2011-11-30T01:06:26Z</updated>
		
		<summary type="html">&lt;p&gt;Georgy Konoplich: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Расстояние Левенштейна''' (также '''редакционное расстояние''' или '''дистанция редактирования''') между двумя строками в теории информации и компьютерной лингвистике — это минимальное количество операций вставки одного символа, удаления одного символа и замены одного символа на другой, необходимых для превращения одной строки в другую.}}&lt;br /&gt;
&lt;br /&gt;
== Свойства ==&lt;br /&gt;
&lt;br /&gt;
Для расстояния Левенштейна справедливы следующие утверждения:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) \ge | |S_1| - |S_2| |&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) \le max( |S_1| , |S_2| )&amp;lt;/tex&amp;gt;&lt;br /&gt;
* &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2) = 0 \Leftrightarrow S_1 = S_2&amp;lt;/tex&amp;gt;&lt;br /&gt;
где &amp;lt;tex&amp;gt;\rm{d}(S_1,S_2)&amp;lt;/tex&amp;gt; — расстояние Левенштейна между строками &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, а |S| - длина строки S.&lt;br /&gt;
&lt;br /&gt;
== Редакционное предписание ==&lt;br /&gt;
&lt;br /&gt;
''Редакционным предписанием'' называется последовательность действий, необходимых для получения из первой строки второй кратчайшим образом. Обычно действия обозначаются так: '''D''' (англ. delete) — удалить, '''I''' (англ. insert) — вставить, '''R''' (англ. replace) — заменить, '''M''' (англ. match) — совпадение.&lt;br /&gt;
&lt;br /&gt;
Например, для 2-х строк «hell123» и «hello214» можно построить следующую таблицу преобразований:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border = &amp;quot;1&amp;quot;&lt;br /&gt;
!'''M''' ||'''M''' ||'''M''' ||'''M''' ||'''R''' ||'''M''' ||'''R''' ||'''I'''&lt;br /&gt;
|-&lt;br /&gt;
|'''h''' ||'''e''' ||'''l''' ||'''l''' ||'''1''' ||'''2''' ||'''3''' ||&lt;br /&gt;
|-&lt;br /&gt;
|'''h''' ||'''e''' ||'''l''' ||'''l''' ||'''o''' ||'''2''' ||'''1''' ||'''4'''&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== код получения редакторского предписания ==&lt;br /&gt;
&lt;br /&gt;
  readln(s1);&lt;br /&gt;
  readln(s2);&lt;br /&gt;
  n := length(s1);&lt;br /&gt;
  m := length(s2);&lt;br /&gt;
  for i := 1 to n do d[0, i] := i;&lt;br /&gt;
  for i := 1 to m do d[i, 0] := i;&lt;br /&gt;
  for i := 1 to n do for j := 1 to m do&lt;br /&gt;
    if s1[i] = s2[j] then&lt;br /&gt;
    begin&lt;br /&gt;
      d[i, j] := d[i - 1, j - 1];&lt;br /&gt;
      p[i, j].x := i - 1;&lt;br /&gt;
      p[i, j].y := j - 1;&lt;br /&gt;
    end else&lt;br /&gt;
    begin&lt;br /&gt;
      if (d[i - 1, j] &amp;lt;= d[i, j - 1])and(d[i - 1, j] &amp;lt;= d[i - 1, j - 1]) then&lt;br /&gt;
      begin&lt;br /&gt;
        d[i, j] := d[i - 1, j] +1;&lt;br /&gt;
        p[i, j].x := i - 1;&lt;br /&gt;
        p[i, j].y := j;&lt;br /&gt;
      end;&lt;br /&gt;
      if (d[i, j - 1] &amp;lt;= d[i - 1, j])and(d[i, j - 1] &amp;lt;= d[i - 1, j - 1]) then&lt;br /&gt;
      begin&lt;br /&gt;
        d[i, j] := d[i, j - 1] +1;&lt;br /&gt;
        p[i, j].x := i;&lt;br /&gt;
        p[i, j].y := j - 1;&lt;br /&gt;
      end;&lt;br /&gt;
      if (d[i - 1, j - 1] &amp;lt;= d[i, j - 1])and(d[i - 1, j - 1] &amp;lt;= d[i - 1, j]) then&lt;br /&gt;
      begin&lt;br /&gt;
        d[i, j] := d[i - 1, j - 1] + 1;&lt;br /&gt;
        p[i, j].x := i - 1;&lt;br /&gt;
        p[i, j].y := j - 1;&lt;br /&gt;
      end;&lt;br /&gt;
    end;&lt;br /&gt;
  x := n;&lt;br /&gt;
  y := m;&lt;br /&gt;
  while (x &amp;gt; 0) and (y &amp;gt; 0) do&lt;br /&gt;
  begin&lt;br /&gt;
   if p[x, y].x - x = 0 then s := 'D' + s else&lt;br /&gt;
   if p[x, y].y - y = 0 then s := 'I' + s else&lt;br /&gt;
   if s1[x] = s2[y] then s := 'M' + s else s := 'R' + s;&lt;br /&gt;
   x := p[x, y].x;&lt;br /&gt;
   y := p[x, y].y;&lt;br /&gt;
  end;&lt;br /&gt;
  writeln(s);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Разные цены операций ===&lt;br /&gt;
&lt;br /&gt;
Цены операций могут зависеть от вида операции (вставка, удаление, замена) и/или от участвующих в ней символов, отражая разную вероятность разных ошибок при вводе текста, и т. п. В общем случае:&lt;br /&gt;
* w(a, b) — цена замены символа a на символ b&lt;br /&gt;
* w(ε, b) — цена вставки символа b&lt;br /&gt;
* w(a, ε) — цена удаления символа a&lt;br /&gt;
&lt;br /&gt;
Для решения задачи о редакционном расстоянии, необходимо найти последовательность замен, минимизирующую суммарную цену. Расстояние Левенштейна является частным случаем этой задачи при&lt;br /&gt;
* w(a, а) = 0&lt;br /&gt;
* w(a, b) = 1 при a≠b&lt;br /&gt;
* w(ε, b) = 1&lt;br /&gt;
* w(a, ε) = 1&lt;br /&gt;
&lt;br /&gt;
Как частный случай, так и задачу для произвольных w, решает алгоритм Вагнера — Фишера, приведённый ниже. Здесь и ниже мы считаем, что все w неотрицательны, и действует правило треугольника: если две последовательные операции можно заменить одной, это не ухудшает общую цену (например, заменить символ x на y, а потом с y на z не лучше, чем сразу x на z).&lt;br /&gt;
&lt;br /&gt;
== Формула ==&lt;br /&gt;
&lt;br /&gt;
Будем считать, что элементы строк нумеруются с первого, как принято в математике, а не нулевого.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; — две строки (длиной &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;N&amp;lt;/tex&amp;gt; соответственно) над некоторым алфавитом, тогда редакционное расстояние &amp;lt;tex&amp;gt;\rm{d}(S_1, S_2)&amp;lt;/tex&amp;gt; можно подсчитать по следующей рекуррентной формуле:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\ \rm{d}(S_1, S_2) = \rm{D}(M,N)&amp;lt;/tex&amp;gt; , где&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\rm{D}(i, j) = \left\{\begin{array}{llcl}&lt;br /&gt;
0&amp;amp;&amp;amp;;&amp;amp;i = 0,\ j = 0\\&lt;br /&gt;
i&amp;amp;&amp;amp;;&amp;amp;j = 0,\ i &amp;gt; 0\\&lt;br /&gt;
j&amp;amp;&amp;amp;;&amp;amp;i = 0,\ j &amp;gt; 0\\&lt;br /&gt;
\rm{min}(\\&lt;br /&gt;
&amp;amp;\rm{D}(i, j - 1) + 1\\&lt;br /&gt;
&amp;amp;\rm{D}(i - 1, j) + 1&amp;amp;;&amp;amp;j &amp;gt; 0,\ i &amp;gt; 0\\&lt;br /&gt;
&amp;amp;\rm{D}(i - 1, j - 1) + \rm{m}(S_1[i], S_2[j])\\&lt;br /&gt;
)&lt;br /&gt;
\end{array}\right.&lt;br /&gt;
&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\rm{m}(a,b)&amp;lt;/tex&amp;gt; равна нулю, если &amp;lt;tex&amp;gt;a = b&amp;lt;/tex&amp;gt; и единице в противном случае; &amp;lt;tex&amp;gt;\min(a, b, c)&amp;lt;/tex&amp;gt; возвращает наименьший из аргументов.&lt;br /&gt;
&lt;br /&gt;
=== Доказательство ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим формулу более подробно. Здесь &amp;lt;tex&amp;gt;D(i, j)&amp;lt;/tex&amp;gt; — расстояние между префиксами строк: первыми i символами строки &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; и первыми j символами строки &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;. Очевидно, что редакционное расстояние между двумя пустыми строками равно нулю. Так же очевидно то, что чтобы получить пустую строку из строки длиной &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, нужно совершить &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; операций удаления, а чтобы получить строку длиной &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; из пустой, нужно произвести &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; операций вставки. Осталось рассмотреть нетривиальный случай, когда обе строки непусты.&lt;br /&gt;
&lt;br /&gt;
Для начала заметим, что в оптимальной последовательности операций, их можно произвольно менять местами. В самом деле, рассмотрим две последовательные операции:&lt;br /&gt;
* Две замены одного и того же символа — неоптимально (если мы заменили x на y, потом y на z, выгоднее было сразу заменить x на z).&lt;br /&gt;
* Две замены разных символов можно менять местами&lt;br /&gt;
* Два стирания или две вставки можно менять местами&lt;br /&gt;
* Вставка символа с его последующим стиранием — неоптимально (можно их обе отменить)&lt;br /&gt;
* Стирание и вставку разных символов можно менять местами&lt;br /&gt;
* Вставка символа с его последующей заменой — неоптимально (излишняя замена)&lt;br /&gt;
* Вставка символа и замена другого символа меняются местами&lt;br /&gt;
* Замена символа с его последующим стиранием — неоптимально (излишняя замена)&lt;br /&gt;
* Стирание символа и замена другого символа меняются местами&lt;br /&gt;
&lt;br /&gt;
Пускай &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; кончается на символ «a», &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; кончается на символ «b». Есть три варианта:&lt;br /&gt;
# Символ «а», на который кончается &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt;, в какой-то момент был стёрт. Сделаем это стирание первой операцией. Тогда мы стёрли символ «a», после чего превратили первые &amp;lt;tex&amp;gt;i-1&amp;lt;/tex&amp;gt; символов &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt; (на что потребовалось &amp;lt;tex&amp;gt;D(i-1,\ j)&amp;lt;/tex&amp;gt; операций), значит, всего потребовалось &amp;lt;tex&amp;gt;D(i-1,\ j)+1&amp;lt;/tex&amp;gt; операций&lt;br /&gt;
# Символ «b», на который кончается &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, в какой-то момент был добавлен. Сделаем это добавление последней операцией. Мы превратили &amp;lt;tex&amp;gt;S_1&amp;lt;/tex&amp;gt; в первые &amp;lt;tex&amp;gt;j-1&amp;lt;/tex&amp;gt; символов &amp;lt;tex&amp;gt;S_2&amp;lt;/tex&amp;gt;, после чего добавили «b». Аналогично предыдущему случаю, потребовалось &amp;lt;tex&amp;gt;D(i,\ j-1)+1&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
# Оба предыдущих утверждения неверны. Если мы добавляли символы справа от финального «a», то чтобы сделать последним символом «b», мы должны были или в какой-то момент добавить его (но тогда утверждение 2 было бы верно), либо заменить на него один из этих добавленных символов (что тоже невозможно, потому что добавление символа с его последующей заменой неоптимально). Значит, символов справа от финального «a» мы не добавляли. Самого финального «a» мы не стирали, поскольку утверждение 1 неверно. Значит, единственный способ изменения последнего символа — его замена. Заменять его 2 или больше раз неоптимально. Значит,&lt;br /&gt;
## Если &amp;lt;tex&amp;gt;a=b&amp;lt;/tex&amp;gt;, мы последний символ не меняли. Поскольку мы его также не стирали и не приписывали ничего справа от него, он не влиял на наши действия, и, значит, мы выполнили &amp;lt;tex&amp;gt;D(i-1,\ j-1)&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
## Если &amp;lt;tex&amp;gt;a\ne b&amp;lt;/tex&amp;gt;, мы последний символ меняли один раз. Сделаем эту замену первой. В дальнейшем, аналогично предыдущему случаю, мы должны выполнить &amp;lt;tex&amp;gt;D(i-1,\ j-1)&amp;lt;/tex&amp;gt; операций, значит, всего потребуется &amp;lt;tex&amp;gt;D(i-1,\ j-1)+1&amp;lt;/tex&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Вагнера — Фишера ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения кратчайшего расстояния необходимо вычислить матрицу D, используя [[#Формула|вышеприведённую формулу]]. Её можно вычислять как по строкам, так и по столбцам. &lt;br /&gt;
Псевдокод алгоритма, написанный при произвольных ценах замен, вставок и удалений (важно помнить, что элементы нумеруются с 1):&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 D(0,0) = 0&lt;br /&gt;
 для всех j от 1 до N&lt;br /&gt;
   D(0,j) = D(0,j-1) + цена вставки символа S2[j]&lt;br /&gt;
 для всех i от 1 до M&lt;br /&gt;
   D(i,0) = D(i-1,0) + цена удаления символа S1[i]&lt;br /&gt;
   для всех j от 1 до N&lt;br /&gt;
     D(i,j) = min(&lt;br /&gt;
        D(i-1, j) + цена удаления символа S1[i],&lt;br /&gt;
        D(i, j-1) + цена вставки символа S2[j],&lt;br /&gt;
        D(i-1, j-1) + цена замены символа S1[i] на символ S2[j]&lt;br /&gt;
     )&lt;br /&gt;
 вернуть D(M,N)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Память ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм в виде, описанном выше, требует &amp;lt;tex&amp;gt;\Theta(M \cdot N)&amp;lt;/tex&amp;gt; операций и такую же память, однако, если требуется только расстояние, легко уменьшить требуемую память до &amp;lt;tex&amp;gt;\Theta(\min(M,N))&amp;lt;/tex&amp;gt;. Для этого надо учесть, что после вычисления любой строки предыдущая строка больше не нужна. Более того, после вычисления D(i, j) не нужны также D(i-1,0) … D(i-1,j-1). Поэтому алгоритм можно переписать как&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 для всех i от 0 до M&lt;br /&gt;
   для всех j от 0 до N&lt;br /&gt;
     вычислить D(i, j)&lt;br /&gt;
     если i&amp;gt;0 и j&amp;gt;0&lt;br /&gt;
       стереть D(i-1, j-1)&lt;br /&gt;
 вернуть D(M, N)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
*http://en.wikipedia.org&lt;br /&gt;
*Романовский И.В. &amp;quot;Дискретный анализ&amp;quot;&lt;/div&gt;</summary>
		<author><name>Georgy Konoplich</name></author>	</entry>

	</feed>