<?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=78.30.250.135&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=78.30.250.135&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/78.30.250.135"/>
		<updated>2026-06-11T14:17:20Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>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&amp;diff=68191</id>
		<title>Дерево поиска, наивная реализация</title>
		<link rel="alternate" type="text/html" href="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&amp;diff=68191"/>
				<updated>2019-01-07T18:38:16Z</updated>
		
		<summary type="html">&lt;p&gt;78.30.250.135: /* См. также Добавлена ссылка на статью про АВЛ-дерево*/&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Файл:Binary_search_tree.svg.png|right|300px|thumb|Бинарное дерево поиска из 9 элементов]]'''Бинарное дерево поиска''' (англ. ''binary search tree, BST'') {{---}} структура данных для работы с [[Упорядоченное множество|упорядоченными множествами]].&lt;br /&gt;
Бинарное дерево поиска обладает следующим свойством: если &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; {{---}} узел бинарного дерева с ключом &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, то все узлы в левом поддереве должны иметь ключи, меньшие &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, а в правом поддереве большие &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;.&lt;br /&gt;
== Операции в бинарном дереве поиска ==&lt;br /&gt;
Для представления бинарного дерева поиска в памяти будем использовать следующую структуру:&lt;br /&gt;
 '''struct''' Node:&lt;br /&gt;
   '''T''' key                    &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// ключ узла&amp;lt;/font&amp;gt;&lt;br /&gt;
   '''Node''' left                &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// указатель на левого потомка&amp;lt;/font&amp;gt;&lt;br /&gt;
   '''Node''' right               &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// указатель на правого потомка&amp;lt;/font&amp;gt;&lt;br /&gt;
   '''Node''' parent              &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// указатель на предка&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Обход дерева поиска ===&lt;br /&gt;
Есть три операции обхода узлов дерева, отличающиеся порядком обхода узлов:&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathrm{inorderTraversal}&amp;lt;/tex&amp;gt; {{---}} обход узлов в отсортированном порядке,&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathrm{preorderTraversal}&amp;lt;/tex&amp;gt; {{---}} обход узлов в порядке: вершина, левое поддерево, правое поддерево,&lt;br /&gt;
* &amp;lt;tex&amp;gt;\mathrm{postorderTraversal}&amp;lt;/tex&amp;gt; {{---}} обход узлов в порядке: левое поддерево, правое поддерево, вершина.&lt;br /&gt;
 '''func''' inorderTraversal(x : '''Node'''):&lt;br /&gt;
    '''if''' x != ''null''&lt;br /&gt;
       inorderTraversal(x.left)&lt;br /&gt;
       '''print''' x.key&lt;br /&gt;
       inorderTraversal(x.right)&lt;br /&gt;
При выполнении данного обхода вершины будут выведены в следующем порядке: ''1 3 4 6 7 8 10 13 14''.&lt;br /&gt;
&lt;br /&gt;
 '''func''' preorderTraversal(x : '''Node''')&lt;br /&gt;
    '''if''' x != ''null''&lt;br /&gt;
       '''print''' x.key&lt;br /&gt;
       preorderTraversal(x.left)&lt;br /&gt;
       preorderTraversal(x.right)&lt;br /&gt;
При выполнении данного обхода вершины будут выведены в следующем порядке: ''8 3 1 6 4 7 10 14 13''.&lt;br /&gt;
&lt;br /&gt;
 '''func''' postorderTraversal(x : '''Node''')&lt;br /&gt;
    '''if''' x != ''null''&lt;br /&gt;
       postorderTraversal(x.left)&lt;br /&gt;
       postorderTraversal(x.right)&lt;br /&gt;
       '''print''' x.key&lt;br /&gt;
При выполнении данного обхода вершины будут выведены в следующем порядке: ''1 4 7 6 3 13 14 10 8''.&lt;br /&gt;
&lt;br /&gt;
Данные алгоритмы выполняют обход за время &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;, поскольку процедура вызывается ровно два раза для каждого узла дерева.&lt;br /&gt;
&lt;br /&gt;
=== Поиск элемента ===&lt;br /&gt;
[[Файл:Bst search.png|frame|right|318px|Поиск элемента 4]]&lt;br /&gt;
Для поиска элемента в бинарном дереве поиска можно воспользоваться следующей функцией, которая принимает в качестве параметров корень дерева и искомый ключ. Для каждого узла функция сравнивает значение его ключа с искомым ключом. Если ключи одинаковы, то функция возвращает текущий узел, в противном случае функция вызывается рекурсивно для левого или правого поддерева. Узлы, которые посещает функция образуют нисходящий путь от корня, так что время ее работы &amp;lt;tex&amp;gt;O(h)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt; {{---}} высота дерева.&lt;br /&gt;
&amp;lt;div style=&amp;quot;width: 65%&amp;quot;&amp;gt;&lt;br /&gt;
 '''Node''' search(x : '''Node''', k : '''T'''):&lt;br /&gt;
    '''if''' x == ''null'' '''or''' k == x.key&lt;br /&gt;
       '''return''' x&lt;br /&gt;
    '''if''' k &amp;lt; x.key&lt;br /&gt;
       '''return''' search(x.left, k)&lt;br /&gt;
    '''else'''&lt;br /&gt;
       '''return''' search(x.right, k)&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Поиск минимума и максимума ===&lt;br /&gt;
Чтобы найти минимальный элемент в бинарном дереве поиска, необходимо просто следовать указателям &amp;lt;tex&amp;gt;left&amp;lt;/tex&amp;gt; от корня дерева, пока не встретится значение &amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;. Если у вершины есть левое поддерево, то по свойству бинарного дерева поиска в нем хранятся все элементы с меньшим ключом. Если его нет, значит эта вершина и есть минимальная. Аналогично ищется и максимальный элемент. Для этого нужно следовать правым указателям.&lt;br /&gt;
 '''Node''' minimum(x : '''Node'''):&lt;br /&gt;
   '''if''' x.left == ''null''&lt;br /&gt;
      '''return''' x&lt;br /&gt;
   '''return''' minimum(x.left)&lt;br /&gt;
&lt;br /&gt;
 '''Node''' maximum(x : '''Node'''):&lt;br /&gt;
   '''if''' x.right == ''null''&lt;br /&gt;
      '''return''' x&lt;br /&gt;
   '''return''' maximum(x.right)&lt;br /&gt;
Данные функции принимают корень поддерева, и возвращают минимальный (максимальный) элемент в поддереве. Обе процедуры выполняются за время &amp;lt;tex&amp;gt;O(h)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Поиск следующего и предыдущего элемента ===&lt;br /&gt;
====Реализация с использованием информации о родителе====&lt;br /&gt;
Если у узла есть правое поддерево, то следующий за ним элемент будет минимальным элементом в этом поддереве. Если у него нет правого поддерева, то нужно следовать вверх, пока не встретим узел, который является левым дочерним узлом своего родителя. Поиск предыдущего выполнятся аналогично. Если у узла есть левое поддерево, то следующий за ним элемент будет максимальным элементом в этом поддереве. Если у него нет левого поддерева, то нужно следовать вверх, пока не встретим узел, который является правым дочерним узлом своего родителя. &lt;br /&gt;
 '''Node''' next(x : '''Node'''):&lt;br /&gt;
    '''if''' x.right != ''null''&lt;br /&gt;
       '''return''' minimum(x.right)&lt;br /&gt;
    y = x.parent&lt;br /&gt;
    '''while''' y != ''null'' '''and''' x == y.right&lt;br /&gt;
       x = y&lt;br /&gt;
       y = y.parent&lt;br /&gt;
    '''return''' y&lt;br /&gt;
&lt;br /&gt;
 '''Node''' prev(x : '''Node'''):&lt;br /&gt;
    '''if''' x.left != ''null''&lt;br /&gt;
       '''return''' maximum(x.left)&lt;br /&gt;
    y = x.parent&lt;br /&gt;
    '''while''' y != ''null'' '''and''' x == y.left&lt;br /&gt;
       x = y&lt;br /&gt;
       y = y.parent&lt;br /&gt;
    '''return''' y&lt;br /&gt;
Обе операции выполняются за время &amp;lt;tex&amp;gt;O(h)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
====Реализация без использования информации о родителе====&lt;br /&gt;
Рассмотрим поиск следующего элемента для некоторого ключа &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Поиск будем начинать с корня дерева, храня текущий узел &amp;lt;tex&amp;gt;current&amp;lt;/tex&amp;gt; и узел &amp;lt;tex&amp;gt;successor&amp;lt;/tex&amp;gt;, последний посещенный узел, ключ которого больше &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
Спускаемся вниз по дереву, как в алгоритме поиска узла. Рассмотрим ключ текущего узла &amp;lt;tex&amp;gt;current&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;current.key \leqslant x&amp;lt;/tex&amp;gt;, значит следующий за &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; узел находится в правом поддереве (в левом поддереве все ключи меньше &amp;lt;tex&amp;gt;current.key&amp;lt;/tex&amp;gt;). Если же &amp;lt;tex&amp;gt;x &amp;lt; current.key&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;x &amp;lt; next(x) \leqslant current.key&amp;lt;/tex&amp;gt;, поэтому &amp;lt;tex&amp;gt;current&amp;lt;/tex&amp;gt; может быть следующим для ключа &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, либо следующий узел содержится в левом поддереве &amp;lt;tex&amp;gt;current&amp;lt;/tex&amp;gt;. Перейдем к нужному поддереву и повторим те же самые действия.&amp;lt;br&amp;gt;&lt;br /&gt;
Аналогично реализуется операция поиска предыдущего элемента.&lt;br /&gt;
 '''Node''' next(x : '''T'''):&lt;br /&gt;
    '''Node''' current = root, successor = ''null''                &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// root {{---}} корень дерева&amp;lt;/font&amp;gt;&lt;br /&gt;
    '''while''' current != ''null''&lt;br /&gt;
       '''if''' current.key &amp;gt; x&lt;br /&gt;
          successor = current&lt;br /&gt;
          current = current.left&lt;br /&gt;
       '''else'''&lt;br /&gt;
          current = current.right&lt;br /&gt;
    '''return''' successor&lt;br /&gt;
=== Вставка ===&lt;br /&gt;
Операция вставки работает аналогично поиску элемента, только при обнаружении у элемента отсутствия ребенка нужно подвесить на него вставляемый элемент.&lt;br /&gt;
====Реализация с использованием информации о родителе====&lt;br /&gt;
 '''func''' insert(x : '''Node''', z : '''Node'''):            &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// x {{---}} корень поддерева, z {{---}} вставляемый элемент&amp;lt;/font&amp;gt;&lt;br /&gt;
    '''while''' x != ''null''&lt;br /&gt;
      '''if''' z.key &amp;gt; x.key&lt;br /&gt;
         '''if''' x.right != ''null''&lt;br /&gt;
            x = x.right&lt;br /&gt;
         '''else'''&lt;br /&gt;
            z.parent = x&lt;br /&gt;
            x.right = z&lt;br /&gt;
            '''break'''&lt;br /&gt;
      '''else if''' z.key &amp;lt; x.key&lt;br /&gt;
         '''if''' x.left != ''null''&lt;br /&gt;
            x = x.left&lt;br /&gt;
         '''else'''&lt;br /&gt;
            z.parent = x&lt;br /&gt;
            x.left = z&lt;br /&gt;
            '''break'''&lt;br /&gt;
====Реализация без использования информации о родителе====&lt;br /&gt;
 '''Node''' insert(x : '''Node''', z : '''T'''):               &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// x {{---}} корень поддерева, z {{---}} вставляемый ключ&amp;lt;/font&amp;gt;&lt;br /&gt;
    '''if''' x == ''null'' &lt;br /&gt;
       '''return''' Node(z)                        &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// подвесим Node с key = z&amp;lt;/font&amp;gt;&lt;br /&gt;
    '''else if''' z &amp;lt; x.key&lt;br /&gt;
       x.left = insert(x.left, z)&lt;br /&gt;
    '''else if''' z &amp;gt; x.key&lt;br /&gt;
       x.right = insert(x.right, z)&lt;br /&gt;
    '''return''' x&lt;br /&gt;
&lt;br /&gt;
Время работы алгоритма для обеих реализаций {{---}} &amp;lt;tex&amp;gt;O(h)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Удаление ===&lt;br /&gt;
====Нерекурсивная реализация====&lt;br /&gt;
Для удаления узла из бинарного дерева поиска нужно рассмотреть три возможные ситуации. Если у узла нет дочерних узлов, то у его родителя нужно просто заменить указатель на &amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;. Если у узла есть только один дочерний узел, то нужно создать новую связь между родителем удаляемого узла и его дочерним узлом. Наконец, если у узла два дочерних узла, то нужно найти следующий за ним элемент (у этого элемента не будет левого потомка), его правого потомка подвесить на место найденного элемента, а удаляемый узел заменить найденным узлом. Таким образом, свойство бинарного дерева поиска не будет нарушено. Данная реализация удаления не увеличивает высоту дерева. Время работы алгоритма {{---}} &amp;lt;tex&amp;gt;O(h)&amp;lt;/tex&amp;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;
 |  [[Файл:Bst_del1.png |581px]]  &lt;br /&gt;
 |-&lt;br /&gt;
 |'''Удаление узла с одним дочерним узлом'''&lt;br /&gt;
 | [[Файл:Bst_del2.png |604px]]  &lt;br /&gt;
 |-&lt;br /&gt;
 |'''Удаление узла с двумя дочерними узлами'''&lt;br /&gt;
 | [[Файл:Bst_del3.png‎|900px]]&lt;br /&gt;
 |}&lt;br /&gt;
 '''func''' delete(t : '''Node''', v : '''Node'''):                 &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; {{---}} дерево, &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; {{---}} удаляемый элемент&amp;lt;/font&amp;gt;&lt;br /&gt;
    p = v.parent                                  &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// предок удаляемого элемента&amp;lt;/font&amp;gt;&lt;br /&gt;
    '''if''' v.left == ''null'' '''and''' v.right == ''null''         &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// первый случай: удаляемый элемент - лист&amp;lt;/font&amp;gt;&lt;br /&gt;
      '''if''' p.left == v&lt;br /&gt;
        p.left = ''null''&lt;br /&gt;
      '''if''' p.right == v&lt;br /&gt;
        p.right = ''null''&lt;br /&gt;
    '''else if''' v.left == ''null'' '''or''' v.right == ''null''     &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// второй случай: удаляемый элемент имеет одного потомка&amp;lt;/font&amp;gt;&lt;br /&gt;
        '''if''' v.left == ''null''                 &lt;br /&gt;
            '''if''' p.left == v&lt;br /&gt;
              p.left = v.right&lt;br /&gt;
            '''else'''&lt;br /&gt;
              p.right = v.right&lt;br /&gt;
            v.right.parent = p &lt;br /&gt;
        '''else'''&lt;br /&gt;
            '''if''' p.left == v&lt;br /&gt;
                p.left = v.left&lt;br /&gt;
            '''else'''&lt;br /&gt;
                p.right = v.left&lt;br /&gt;
            v.left.parent = p&lt;br /&gt;
    '''else'''                                          &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// третий случай: удаляемый элемент имеет двух потомков&amp;lt;/font&amp;gt;&lt;br /&gt;
      successor = next(v, t)                   &lt;br /&gt;
      v.key = successor.key&lt;br /&gt;
      '''if''' successor.parent.left == successor&lt;br /&gt;
        successor.parent.left = successor.right&lt;br /&gt;
        '''if''' successor.right != ''null''&lt;br /&gt;
          successor.right.parent = successor.parent&lt;br /&gt;
      '''else'''&lt;br /&gt;
        successor.parent.right = successor.left&lt;br /&gt;
        '''if''' successor.left != ''null''&lt;br /&gt;
          successor.right.parent = successor.parent&lt;br /&gt;
&lt;br /&gt;
====Рекурсивная реализация====&lt;br /&gt;
При рекурсивном удалении узла из бинарного дерева нужно рассмотреть три случая: удаляемый элемент находится в левом поддереве текущего поддерева, удаляемый элемент находится в правом поддереве или удаляемый элемент находится в корне. В двух первых случаях нужно рекурсивно удалить элемент из нужного поддерева. Если удаляемый элемент находится в корне текущего поддерева и имеет два дочерних узла, то нужно заменить его минимальным элементом из правого поддерева и рекурсивно удалить '''этот''' минимальный элемент из правого поддерева. Иначе, если удаляемый элемент имеет один дочерний узел, нужно заменить его потомком. Время работы алгоритма {{---}} &amp;lt;tex&amp;gt;O(h)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Рекурсивная функция, возвращающая дерево с удаленным элементом &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;:&lt;br /&gt;
 '''Node''' delete(root : '''Node''', z : '''T'''):               &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// корень поддерева, удаляемый ключ&amp;lt;/font&amp;gt;&lt;br /&gt;
   '''if''' root == ''null''&lt;br /&gt;
     '''return''' root&lt;br /&gt;
   '''if''' z &amp;lt; root.key&lt;br /&gt;
     root.left = delete(root.left, z)&lt;br /&gt;
   '''else if''' z &amp;gt; root.key&lt;br /&gt;
     root.right = delete(root.right, z)&lt;br /&gt;
   '''else if''' root.left != ''null'' '''and''' root.right != ''null''&lt;br /&gt;
     root.key = minimum(root.right).key&lt;br /&gt;
     root.right = delete(root.right, root.key)&lt;br /&gt;
   '''else'''&lt;br /&gt;
     '''if''' root.left != ''null''&lt;br /&gt;
       root = root.left&lt;br /&gt;
     '''else'''&lt;br /&gt;
       root = root.right&lt;br /&gt;
   '''return''' root&lt;br /&gt;
&lt;br /&gt;
==Задачи о бинарном дереве поиска==&lt;br /&gt;
&lt;br /&gt;
===Проверка того, что заданное дерево является деревом поиска===&lt;br /&gt;
&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Определить, является ли заданное двоичное дерево деревом поиска.&lt;br /&gt;
}}&lt;br /&gt;
[[Файл:Not_Enough.png|right|thumb|291px|Пример дерева, для которого недостаточно проверки лишь его соседних вершин]]&lt;br /&gt;
Для того чтобы решить эту задачу, применим [[Обход в глубину, цвета вершин|обход в глубину]]. Запустим от корня рекурсивную логическую функцию, которая выведет &amp;lt;tex&amp;gt;\mathtt{true}&amp;lt;/tex&amp;gt;, если дерево является BST и &amp;lt;tex&amp;gt;\mathtt{false}&amp;lt;/tex&amp;gt; в противном случае. Чтобы дерево не являлось BST, в нём должна быть хотя бы одна вершина, которая не попадает под определение дерева поиска. То есть достаточно найти всего одну такую вершину, чтобы выйти из рекурсии и вернуть значение &amp;lt;tex&amp;gt;\mathtt{false}&amp;lt;/tex&amp;gt;. Если же, дойдя до листьев, функция не встретит на своём пути такие вершины, она вернёт значение &amp;lt;tex&amp;gt;\mathtt{true}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Функция принимает на вход исследуемую вершину, а также два значения: &amp;lt;tex&amp;gt;\mathtt{min}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{max}&amp;lt;/tex&amp;gt;, которые до вызова функции равнялись &amp;lt;tex&amp;gt; \infty &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; -\infty &amp;lt;/tex&amp;gt; соответственно, где &amp;lt;tex&amp;gt; \infty &amp;lt;/tex&amp;gt; — очень большое число, т.е. ни один ключ дерева не превосходит его по модулю. Казалось бы, два последних параметра не нужны. Но без них программа может выдать неверный ответ, так как сравнения только вершины и её детей недостаточно. Необходимо также помнить, в каком поддереве для более старших предков мы находимся. Например, в этом дереве вершина с номером &amp;lt;tex&amp;gt;8&amp;lt;/tex&amp;gt; находится левее вершины, в которой лежит &amp;lt;tex&amp;gt;5&amp;lt;/tex&amp;gt;, чего не должно быть в дереве поиска, однако после проверки функция бы вернула &amp;lt;tex&amp;gt;\mathtt{true}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 '''bool''' isBinarySearchTree(root: '''Node'''):                    &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// Здесь root — корень заданного двоичного дерева.&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
   '''bool''' check(v : '''Node''', min: '''T''', max: '''T'''):                 &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// min и max — минимально и максимально допустимые значения в вершинах поддерева.&amp;lt;/font&amp;gt;&lt;br /&gt;
     '''if''' v == ''null''                    '''return''' ''true''&lt;br /&gt;
     '''if''' v.key &amp;lt;= min '''or''' max &amp;lt;= v.key '''return''' ''false''&lt;br /&gt;
     '''return''' check(v.left, min, v.key) '''and''' check(v.right, v.key, max)&lt;br /&gt;
 &lt;br /&gt;
   '''return''' check(root, &amp;lt;tex&amp;gt; -\infty &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \infty &amp;lt;/tex&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Время работы алгоритма {{---}} &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; {{---}} количество вершин в дереве.&lt;br /&gt;
&lt;br /&gt;
===Задачи на поиск максимального BST в заданном двоичном дереве===&lt;br /&gt;
&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Найти в данном дереве такую вершину, что она будет корнем поддерева поиска с наибольшим количеством вершин.&lt;br /&gt;
}}&lt;br /&gt;
Если мы будем приведённым выше способом проверять каждую вершину, мы справимся с задачей за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;. Но её можно решить за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;, идя от корня и проверяя все вершины по одному разу, основываясь на следующих фактах:&lt;br /&gt;
* Значение в вершине больше максимума в её левом поддереве;&lt;br /&gt;
* Значение в вершине меньше минимума в её правом поддереве;&lt;br /&gt;
* Левое и правое поддерево являются деревьями поиска.&lt;br /&gt;
&lt;br /&gt;
Введём &amp;lt;tex&amp;gt;\mathtt{v.min}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{v.max}&amp;lt;/tex&amp;gt;, которые будут хранить минимум в левом поддереве вершины и максимум в правом. Тогда мы должны будем проверить, являются ли эти поддеревья деревьями поиска и, если да, лежит ли ключ вершины &amp;lt;tex&amp;gt;\mathtt{v}&amp;lt;/tex&amp;gt; между этими значениями &amp;lt;tex&amp;gt;\mathtt{v.min}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{v.max}&amp;lt;/tex&amp;gt;.  Если вершина является листом, она автоматически становится деревом поиска, а её ключ {{---}} минимумом или максимумом для её родителя (в зависимости от расположения вершины). Функция &amp;lt;tex&amp;gt;\mathtt{cnt}&amp;lt;/tex&amp;gt; записывает в &amp;lt;tex&amp;gt;\mathtt{v.kol}&amp;lt;/tex&amp;gt; количество вершин в дереве, если оно является деревом поиска или &amp;lt;tex&amp;gt;\mathtt{-1}&amp;lt;/tex&amp;gt; в противном случае. После выполнения функции ищем за линейное время вершину с наибольшим значением &amp;lt;tex&amp;gt;\mathtt{v.kol}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 '''int''' count(root: '''Node'''):                 &amp;lt;font color=&amp;quot;green&amp;quot;&amp;gt;// root — корень заданного двоичного дерева.&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
   '''int''' cnt(v: '''Node'''):&lt;br /&gt;
     '''if''' v == ''null''&lt;br /&gt;
       v.kol = 0&lt;br /&gt;
       '''return''' = 0&lt;br /&gt;
     '''if''' cnt(v.left) != -1 '''and''' cnt(v.right) != -1&lt;br /&gt;
       '''if''' v.left == ''null'' '''and''' v.right == ''null''&lt;br /&gt;
         v.min = v.key&lt;br /&gt;
         v.max = v.key&lt;br /&gt;
         v.kol = 1&lt;br /&gt;
         '''return''' 1&lt;br /&gt;
       '''if''' v.left == ''null''&lt;br /&gt;
         '''if''' v.right.max &amp;gt; v.key&lt;br /&gt;
           v.min = v.key&lt;br /&gt;
           v.kol = cnt(v.right) + 1&lt;br /&gt;
           '''return''' v.kol&lt;br /&gt;
       '''if''' v.right == ''null''&lt;br /&gt;
         '''if''' v.left.min &amp;lt; v.key&lt;br /&gt;
           v.max = v.key&lt;br /&gt;
           v.kol = cnt(v.left) + 1&lt;br /&gt;
           '''return''' v.kol&lt;br /&gt;
       '''if''' v.left.min &amp;lt; v.key '''and''' v.right.max &amp;gt; v.key &lt;br /&gt;
         v.min = v.left.min&lt;br /&gt;
         v.max = v.right.max&lt;br /&gt;
         v.kol = v.left.kol + v.right.kol + 1&lt;br /&gt;
         v.kol = cnt(v.left) + cnt(v.right) + 1&lt;br /&gt;
         '''return''' v.kol&lt;br /&gt;
     '''return''' -1&lt;br /&gt;
 &lt;br /&gt;
   '''return''' cnt(root)&lt;br /&gt;
&lt;br /&gt;
Алгоритм работает за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;, так как мы прошлись по дереву два раза за время, равное количеству вершин.&lt;br /&gt;
&lt;br /&gt;
===Восстановление дерева по результату обхода preorderTraversal===&lt;br /&gt;
&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Восстановить дерево по последовательности, выведенной после выполнения процедуры &amp;lt;tex&amp;gt;\mathrm{preorderTraversal}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
[[Файл:BST_from_seq.gif|right|thumb|257px|Восстановление дерева поиска по последовательности ключей]]&lt;br /&gt;
&lt;br /&gt;
Как мы помним, процедура &amp;lt;tex&amp;gt;\mathrm{preorderTraversal}&amp;lt;/tex&amp;gt; выводит значения в узлах поддерева следующим образом: сначала идёт до упора влево, затем на каком-то моменте делает шаг вправо и снова движется влево. Это продолжается до тех пор, пока не будут выведены все вершины. Полученная последовательность позволит нам однозначно определить расположение всех узлов поддерева. Первая вершина всегда будет в корне. Затем, пока не будут использованы все значения, будем последовательно подвешивать левых сыновей к последней добавленной вершине, пока не найдём номер, нарушающий убывающую последовательность, а для каждого такого номера будем искать вершину без правого потомка, хранящую наибольшее значение, не превосходящее того, которое хотим поставить, и подвешиваем к ней элемент с таким номером в качестве правого сына. Когда мы, желая найти такую вершину, встречаем какую-нибудь другую, уже имеющую правого сына, проходим по ветке вправо. Мы имеем на это право, так как если такая вершина стоит, то процедура обхода в ней уже побывала и поворачивала вправо, поэтому спускаться в другую сторону смысла не имеет. Вершину с максимальным ключом, с которой будем начинать поиск, будем запоминать. Она будет обновляться каждый раз, когда появится новый максимум.&lt;br /&gt;
&lt;br /&gt;
Процедура восстановления дерева работает за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Разберём алгоритм на примере последовательности &amp;lt;tex&amp;gt;\mathtt{8}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\mathtt{2}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\mathtt{1}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\mathtt{4}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\mathtt{3}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\mathtt{5}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Будем выделять красным цветом вершины, рассматриваемые на каждом шаге, чёрным жирным {{---}} их родителей, курсивом {{---}} убывающие подпоследовательности (в случаях, когда мы их рассматриваем) или претендентов на добавление к ним правого ребёнка (когда рассматривается вершина, нарушающая убывающую последовательность).&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Состояние &lt;br /&gt;
последовательности&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Действие&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Пояснение&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;|  &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;'''8'''&amp;lt;/span&amp;gt; 2 1 4 3 5&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| Делаем вершину корнем.&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| ''Первая вершина всегда будет корнем, так как вывод начинался с него.''&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| '''''8''''' &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;'''''2'''''&amp;lt;/span&amp;gt; ''1'' 4 3 5&lt;br /&gt;
|rowspan=2 style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;|Находим убывающую подпоследовательность. Каждую вершину подвешиваем к последней из взятых ранее в качестве левого сына.&lt;br /&gt;
|rowspan=2 style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| ''Каждая последующая вершина становится левым сыном предыдущей, так как выводя ключи, мы двигались по дереву поиска влево, пока есть вершины.''&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| ''8 '''2''''' &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;'''''1'''''&amp;lt;/span&amp;gt; 4 3 5&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| ''8 '''2''''' 1 &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;'''4'''&amp;lt;/span&amp;gt; 3 5&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| Для вершины, нарушившей убывающую последовательность, ищем максимальное значение, меньшее его. В данном случае оно равно &amp;lt;tex&amp;gt;\mathtt{2}&amp;lt;/tex&amp;gt;. Затем добавляем вершину.&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| ''На моменте вывода следующего номера процедура обратилась уже к какому-то из правых поддеревьев, так как влево идти уже некуда. Значит, нам необходимо найти узел, для которого данная вершина являлась бы правым сыном. Очевидно, что в её родителе не может лежать значение, которое больше её ключа. Но эту вершину нельзя подвесить и к меньшим, иначе нашёлся бы более старший предок, также хранящий какое-то значение, которое меньше, чем в исследуемой. Для этого предка вершина бы попала в левое поддерево. И тогда возникает противоречие с определением дерева поиска. Отсюда следует, что родитель определяется единственным образом {{---}} он хранит максимум среди ключей, не превосходящих значения в подвешиваемой вершине, что и требовалось доказать.'' &lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| 8 2 1 '''''4''''' &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;'''''3'''''&amp;lt;/span&amp;gt; 5&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| Находим убывающую подпоследовательность. Каждую вершину подвешиваем к последней из взятых ранее в качестве левого сына.&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| ''Зайдя в правое поддерево, процедура обхода снова до упора начала двигаться влево, поэтому действуем аналогичным образом.''&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| ''8'' 2 1 '''''4''''' 3 &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;'''5'''&amp;lt;/span&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| Для этой вершины ищем максимальное значение, меньшее его. Затем добавляем вершину.&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 10px&amp;quot;| ''Здесь процедура снова обратилась к правому поддереву. Рассуждения аналогичны. Ключ родителя этой вершины равен &amp;lt;tex&amp;gt;\mathtt{4}&amp;lt;/tex&amp;gt;.'' &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Поисковые структуры данных]]&lt;br /&gt;
* [[Рандомизированное бинарное дерево поиска]]&lt;br /&gt;
* [[Красно-черное дерево]]&lt;br /&gt;
* [[АВЛ-дерево]]&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
* [https://ru.wikipedia.org/wiki/%D0%94%D0%B2%D0%BE%D0%B8%D1%87%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0 Википедия {{---}} Двоичное дерево поиска]&lt;br /&gt;
* [https://en.wikipedia.org/wiki/Binary_search_tree Wikipedia {{---}} Binary search tree]&lt;br /&gt;
* Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн, К. Алгоритмы: построение и анализ = Introduction to Algorithms / Под ред. И. В. Красикова. — 2-е изд. — М.: Вильямс, 2005. — 1296 с. — ISBN 5-8459-0857-4&lt;br /&gt;
&lt;br /&gt;
[[Категория: Деревья поиска]]&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>78.30.250.135</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D0%BE%D0%B8%D1%87%D0%BD%D0%B0%D1%8F_%D0%BA%D1%83%D1%87%D0%B0&amp;diff=68177</id>
		<title>Двоичная куча</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B2%D0%BE%D0%B8%D1%87%D0%BD%D0%B0%D1%8F_%D0%BA%D1%83%D1%87%D0%B0&amp;diff=68177"/>
				<updated>2019-01-06T14:04:27Z</updated>
		
		<summary type="html">&lt;p&gt;78.30.250.135: /* Построение кучи за O(n) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Определение==&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Двоичная куча''' или '''пирамида''' (англ. ''Binary heap'') — такое двоичное [[Дерево, эквивалентные определения|подвешенное дерево]], для которого выполнены следующие три условия:&lt;br /&gt;
&lt;br /&gt;
* Значение в любой вершине не меньше (если куча для максимума), чем значения её потомков.&lt;br /&gt;
* На &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ом слое &amp;lt;tex&amp;gt;2^i&amp;lt;/tex&amp;gt; вершин, кроме последнего. Слои нумеруются с нуля.&lt;br /&gt;
* Последний слой заполнен слева направо (как показано на рисунке)&lt;br /&gt;
}}&lt;br /&gt;
[[Файл:Min_heap.png‎|thumb|325px|Пример кучи для минимума]]&lt;br /&gt;
[[Файл:Min_heap_array.png‎|thumb|325px|Хранение кучи в массиве, красная стрелка {{---}} левый сын, зеленая {{---}} правый]]&lt;br /&gt;
Удобнее всего двоичную кучу хранить в виде массива &amp;lt;tex&amp;gt;a[0..n-1]&amp;lt;/tex&amp;gt;, у которого нулевой элемент, &amp;lt;tex&amp;gt;a[0]&amp;lt;/tex&amp;gt; — элемент в корне, а потомками элемента &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; являются &amp;lt;tex&amp;gt;a[2i+1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;a[2i+2]&amp;lt;/tex&amp;gt;. Высота кучи определяется как высота двоичного дерева. То есть она равна количеству рёбер в самом длинном простом пути, соединяющем корень кучи с одним из её листьев. Высота кучи есть &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; — количество узлов дерева.&lt;br /&gt;
&lt;br /&gt;
Чаще всего используют кучи для минимума (когда предок не больше детей) и для максимума (когда предок не меньше детей).&lt;br /&gt;
&lt;br /&gt;
Двоичные кучи используют, например, для того, чтобы извлекать минимум из набора чисел за &amp;lt;tex&amp;gt;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;
Если в куче изменяется один из элементов, то она может перестать удовлетворять свойству упорядоченности. Для восстановления этого свойства служат процедуры &amp;lt;tex&amp;gt; \mathrm {siftDown} &amp;lt;/tex&amp;gt; (просеивание вниз)&lt;br /&gt;
и &amp;lt;tex&amp;gt; \mathrm {siftUp} &amp;lt;/tex&amp;gt; (просеивание вверх). &lt;br /&gt;
&lt;br /&gt;
====siftDown====&lt;br /&gt;
Если значение измененного элемента увеличивается, то свойства кучи восстанавливаются функцией &amp;lt;tex&amp;gt; \mathrm {siftDown} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&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; \mathrm {siftDown} &amp;lt;/tex&amp;gt; для этого сына.&lt;br /&gt;
Процедура выполняется за время &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' siftDown(i : '''int'''):&lt;br /&gt;
     '''while''' 2 * i + 1 &amp;lt; a.heapSize     &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;// heapSize {{---}} количество элементов в куче&amp;lt;/font&amp;gt;&lt;br /&gt;
         left = 2 * i + 1             &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;// left {{---}} левый сын&amp;lt;/font&amp;gt;&lt;br /&gt;
         right = 2 * i + 2            &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;// right {{---}} правый сын&amp;lt;/font&amp;gt;&lt;br /&gt;
         j = left&lt;br /&gt;
         '''if''' right &amp;lt; a.heapSize '''and''' a[right] &amp;lt; a[left]&lt;br /&gt;
             j = right&lt;br /&gt;
         '''if''' a[i] &amp;lt;= a[j]&lt;br /&gt;
             '''break'''&lt;br /&gt;
         swap(a[i], a[j])&lt;br /&gt;
         i = j&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====siftUp====&lt;br /&gt;
Если значение измененного элемента уменьшается, то свойства кучи восстанавливаются функцией &amp;lt;tex&amp;gt; \mathrm {siftUp} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Работа процедуры: если элемент больше своего отца, условие 1 соблюдено для всего дерева, и больше ничего делать не нужно. Иначе, мы меняем местами его с отцом. После чего выполняем &amp;lt;tex&amp;gt; \mathrm {siftUp} &amp;lt;/tex&amp;gt;&lt;br /&gt;
для этого отца. Иными словами, слишком маленький элемент всплывает наверх.&lt;br /&gt;
Процедура выполняется за время &amp;lt;tex&amp;gt;O(\log{n})&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' siftUp(i : '''int'''):&lt;br /&gt;
     '''while''' a[i] &amp;lt; a[(i - 1) / 2]     &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;// i &amp;lt;tex&amp;gt;==&amp;lt;/tex&amp;gt; 0 {{---}} мы в корне&amp;lt;/font&amp;gt;&lt;br /&gt;
         swap(a[i], a[(i - 1) / 2])&lt;br /&gt;
         i = (i - 1) / 2&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;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Извлечение выполняется в четыре этапа:&lt;br /&gt;
# Значение корневого элемента (он и является минимальным) сохраняется для последующего возврата.&lt;br /&gt;
# Последний элемент копируется в корень, после чего удаляется из кучи.&lt;br /&gt;
# Вызывается &amp;lt;math&amp;gt; \mathrm {siftDown} &amp;lt;/math&amp;gt; для корня.&lt;br /&gt;
# Сохранённый элемент возвращается.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''int''' extractMin():&lt;br /&gt;
     '''int''' min = a[0]&lt;br /&gt;
     a[0] = a[a.heapSize - 1]&lt;br /&gt;
     a.heapSize = a.heapSize - 1&lt;br /&gt;
     siftDown(0)&lt;br /&gt;
     '''return''' min&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;O(\log{n})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Добавление произвольного элемента в конец кучи, и восстановление свойства упорядоченности с помощью процедуры &amp;lt;math&amp;gt; \mathrm {siftUp} &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' insert(key : '''int'''):&lt;br /&gt;
     a.heapSize = a.heapSize + 1&lt;br /&gt;
     a[a.heapSize - 1] = key&lt;br /&gt;
     siftUp(a.heapSize - 1)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Построение кучи за O(n) ===&lt;br /&gt;
{{Определение | definition =&lt;br /&gt;
'''&amp;lt;tex&amp;gt;D&amp;lt;/tex&amp;gt;-куча''' {{---}} это куча, в которой у каждого элемента, кроме, возможно, элементов на последнем уровне, ровно &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; потомков. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Дан массив &amp;lt;tex&amp;gt;a[0.. n - 1].&amp;lt;/tex&amp;gt; Требуется построить &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-кучу с минимумом в корне. Наиболее очевидный способ построить такую кучу из неупорядоченного массива {{---}} сделать нулевой элемент массива корнем, а дальше по очереди добавить все его элементы в конец кучи и запускать от каждого добавленного элемента &amp;lt;math&amp;gt;\mathrm {siftUp}&amp;lt;/math&amp;gt;. Временная оценка такого алгоритма &amp;lt;tex&amp;gt; O(n\log{n})&amp;lt;/tex&amp;gt;. Однако можно построить кучу еще быстрее — за &amp;lt;tex&amp;gt; O(n) &amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Представим, что в массиве хранится дерево (&amp;lt;tex&amp;gt;a[0] - &amp;lt;/tex&amp;gt;  корень, а потомками элемента &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; являются &amp;lt;tex&amp;gt;a[di+1]...a[di+d]&amp;lt;/tex&amp;gt;). Сделаем &amp;lt;tex&amp;gt; \mathrm {siftDown} &amp;lt;/tex&amp;gt; для вершин, имеющих хотя бы одного потомка: от &amp;lt;tex dpi=140&amp;gt;\dfrac{n}{d}&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;,{{---}} так как поддеревья, состоящие из одной вершины без потомков, уже упорядочены.&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= На выходе получим искомую кучу. &lt;br /&gt;
|proof= При вызове &amp;lt;tex&amp;gt; \mathrm {siftDown} &amp;lt;/tex&amp;gt; для вершины, ее поддеревья являются кучами. После выполнения &amp;lt;tex&amp;gt; \mathrm {siftDown} &amp;lt;/tex&amp;gt; эта вершина с ее поддеревьями будут также являться кучей.  Значит, после выполнения всех &amp;lt;tex&amp;gt; \mathrm {siftDown} &amp;lt;/tex&amp;gt; получится куча.&lt;br /&gt;
}}&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= Время работы этого алгоритма &amp;lt;tex&amp;gt; O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof= Число вершин на высоте &amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt; в куче из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов не превосходит &amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt;  \left \lceil \frac{n}{d^h} \right \rceil &amp;lt;/tex&amp;gt;.  Высота кучи не превосходит &amp;lt;tex&amp;gt; \log_{d}n &amp;lt;/tex&amp;gt;. Обозначим за &amp;lt;tex&amp;gt; H &amp;lt;/tex&amp;gt; высоту дерева, тогда время построения не превосходит&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt; \sum_{h = 1}^H  \limits\frac{n}{d^h} \cdot d &amp;lt;/tex&amp;gt; &amp;lt;tex dpi = &amp;quot;150&amp;quot;&amp;gt;  \cdot  h &amp;lt;/tex&amp;gt; &amp;lt;tex  dpi = &amp;quot;160&amp;quot;&amp;gt; = n \cdot d \cdot {\sum_{h = 1}^H  \limits}\frac{h}{d^h}. &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Докажем вспомогательную лемму о сумме ряда.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement= &amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt; {\sum_{h = 1}^\infty  \limits}\frac{h}{d^h} = \frac{d}{(d - 1)^2} . &amp;lt;/tex&amp;gt; &lt;br /&gt;
|proof=&lt;br /&gt;
 Обозначим за &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; сумму ряда. Заметим, что&lt;br /&gt;
&amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt; \frac{n}{d^n} = \frac{1}{d} \cdot \frac{n - 1}{d ^{n - 1}} + \frac{1}{d^n}. &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt;{\sum_{n = 1}^\infty  \limits}\frac{1}{d^n}&amp;lt;/tex&amp;gt; {{---}} это сумма бесконечной убывающей геометрической прогрессии, и она равна &amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt; &lt;br /&gt;
\frac{\frac{1}{d}}{1 - \frac{1}{d}} = \frac{1}{d - 1}. &amp;lt;/tex&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Получаем  &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; &amp;lt;tex dpi = &amp;quot;160&amp;quot; &amp;gt;=\frac{1}{d}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\cdot s +&amp;lt;/tex&amp;gt;  &amp;lt;tex dpi = &amp;quot;160&amp;quot; &amp;gt; \frac{1}{d - 1}. &amp;lt;/tex&amp;gt; Откуда &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; &amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt;  = \frac{d}{(d - 1)^2}. &amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Подставляя в нашу формулу результат леммы, получаем &amp;lt;tex &amp;gt;n&amp;lt;/tex&amp;gt; &amp;lt;tex dpi = &amp;quot;160&amp;quot;&amp;gt;\cdot (\frac {d}{d - 1})^2 &amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;  \leqslant  4 \cdot n &amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;=O(n).&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Псевдокод алгоритма:&lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' buldHeap():&lt;br /&gt;
     '''for''' i = a.heapSize / 2 '''downto''' 0&lt;br /&gt;
         siftDown(i)&lt;br /&gt;
&amp;lt;/code&amp;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;b&amp;lt;/tex&amp;gt;, размерами &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt;, требуется объединить эти две кучи. &lt;br /&gt;
====Наивная реализация====&lt;br /&gt;
Поочередно добавим все элементы из &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;. Время работы {{---}} &amp;lt;tex&amp;gt;O(m \log(n+m))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' merge(a, b : '''Heap'''):&lt;br /&gt;
     '''while''' b.heapSize &amp;gt; 0  &lt;br /&gt;
         a.insert(b.extractMin())&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Реализация с помощью построения кучи====&lt;br /&gt;
Добавим все элементы кучи &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt; в конец массива &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, после чего вызовем функцию построения кучи. Процедура выполняется за время &amp;lt;tex&amp;gt;O(n + m)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code style=&amp;quot;display:inline-block&amp;quot;&amp;gt;&lt;br /&gt;
 '''function''' merge(a, b : '''Heap'''):&lt;br /&gt;
     '''for''' i = 0 '''to''' b.heapSize - 1  &lt;br /&gt;
         a.heapSize = a.heapSize + 1&lt;br /&gt;
         a[a.heapSize - 1] = b[i]&lt;br /&gt;
     a.heapify()&lt;br /&gt;
&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
===Поиск k-ого элемента===&lt;br /&gt;
Требуется найти &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-ый по величине элемент в куче.&lt;br /&gt;
&lt;br /&gt;
# Создаем новую кучу, в которой будем хранить пару &amp;lt;tex&amp;gt;\langle \mathtt{value}, \mathtt{index} \rangle&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathtt{value}&amp;lt;/tex&amp;gt; {{---}} значение элемента, а &amp;lt;tex&amp;gt;\mathtt{index}&amp;lt;/tex&amp;gt; {{---}} индекс элемента в основном массиве, и добавляем в нее корень кучи. &lt;br /&gt;
# Возьмем корень новой кучи и добавим её детей из основной кучи, после чего удалим корень. Проделаем этот шаг &amp;lt;tex&amp;gt;k - 1&amp;lt;/tex&amp;gt; раз.&lt;br /&gt;
# В корне новой кучи будет находиться ответ.&lt;br /&gt;
&lt;br /&gt;
Время работы алгоритма {{---}} &amp;lt;tex&amp;gt;O(k \log k)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При &amp;lt;tex&amp;gt;n \lessapprox k \log k &amp;lt;/tex&amp;gt; выгоднее запускать [[поиск k-ой порядковой статистики]].&lt;br /&gt;
[[Файл:Min_heap_kth.png‎|thumb|center|650px|Пример при &amp;lt;tex&amp;gt;k = 5&amp;lt;/tex&amp;gt;, красные {{---}} уже удаленные из кучи элементы, зеленые находятся в куче, а голубые {{---}} еще не рассмотрены.]]&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Биномиальная куча]]&lt;br /&gt;
* [[Фибоначчиева куча]]&lt;br /&gt;
* [[Левосторонняя куча]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* [[wikipedia:ru:Двоичная куча|Википедия {{---}} Двоичная куча]]&lt;br /&gt;
* [[wikipedia:ru:Очередь с приоритетом|Википедия {{---}} Очередь с приоритетом]]&lt;br /&gt;
* [[wikipedia:en:Binary heap|Wikipedia {{---}} Binary heap]]&lt;br /&gt;
* [[wikipedia:en:Priority queue|Wikipedia {{---}} Priority queue]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Приоритетные очереди]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>78.30.250.135</name></author>	</entry>

	</feed>