Рандомизированное бинарное дерево поиска — различия между версиями
Dima (обсуждение | вклад) |
|||
Строка 2: | Строка 2: | ||
==Основная идея и связанные определения== | ==Основная идея и связанные определения== | ||
− | Как известно, можно подобрать такую последовательность операций с [[Дерево поиска, наивная реализация|бинарным деревом поиска в наивной реализации]], что его глубина будет пропорциональна количеству ключей, | + | Как известно, можно подобрать такую последовательность операций с [[Дерево поиска, наивная реализация|бинарным деревом поиска в наивной реализации]], что его глубина будет пропорциональна количеству ключей, а следовательно запрос будет выполняться за <tex>O(n)</tex>. Поэтому, если поддерживать инвариант "случайности" в дереве, то можно добиться того, что математическое ожидание глубины дерева будет небольшим. |
− | Дадим рекурсивное определение ''' | + | Дадим рекурсивное определение '''рандомизированного бинарного дерева поиска (RBST)'''. |
{{Определение | {{Определение | ||
|definition= | |definition= | ||
Пусть <tex>T</tex> {{---}} бинарное дерево поиска. Тогда | Пусть <tex>T</tex> {{---}} бинарное дерево поиска. Тогда | ||
− | + | # Если <tex>T</tex> пусто, то оно является рандомизированным бинарным деревом поиска. | |
− | + | # Если <tex>T</tex> непусто (содержит <tex>n</tex> вершин, <tex>n > 0</tex>), то <tex>T</tex> {{---}} рандомизированное бинарное дерево поиска тогда и только тогда, когда его левое и правое поддеревья (<tex>L</tex> и <tex>R</tex>) оба являются RBST, а также выполняется соотношение | |
<tex>P[size(L) = i] = \frac{1}n, i = 1..n</tex>. | <tex>P[size(L) = i] = \frac{1}n, i = 1..n</tex>. | ||
}} | }} | ||
− | Из определения следует, что каждый ключ в | + | Из определения следует, что каждый ключ в RBST размера n может оказаться корнем с вероятностью 1/n. |
− | Идея RBST состоит в том, что хранимое дерево постоянно является | + | Идея RBST состоит в том, что хранимое дерево постоянно является рандомизированным бинарным деревом поиска. Далее подробно будет описана реализация операций над RBST, которая позволит добиться этой цели. Заметим лишь, что хранение RBST в памяти ничем не отличается от хранения обычного дерева поиска: хранится указатель на корень; в каждой вершине хранятся указатели на её сыновей. |
(Похожие идеи используются в [[Декартово дерево| декартовом дереве]], поэтому во многих русскоязычных ресурсах термин '''рандомизированное бинарное дерево поиска''' используется как синонимическое название декартового дерева и [[Декартово дерево по неявному ключу|декартового дерева по неявному ключу]]) | (Похожие идеи используются в [[Декартово дерево| декартовом дереве]], поэтому во многих русскоязычных ресурсах термин '''рандомизированное бинарное дерево поиска''' используется как синонимическое название декартового дерева и [[Декартово дерево по неявному ключу|декартового дерева по неявному ключу]]) | ||
Строка 19: | Строка 19: | ||
==Операции== | ==Операции== | ||
− | Операции, | + | Операции обхода дерева, поиска ключа, поиска максимума/минимума, поиск следующего/предыдущего элемента выполняются как в [[Дерево поиска, наивная реализация|обычном дереве поиска]], т.к. не меняют структуру дерева. |
===Вставка=== | ===Вставка=== | ||
− | Рассмотрим рекурсивный алгоритм вставки ключа <tex>x</tex> в RBST состоящее из <tex>n</tex> вершин. С вероятностью <tex dpi = "150">\frac{1}{n+1}</tex> вставим ключ в корень дерева, | + | Рассмотрим рекурсивный алгоритм вставки ключа <tex>x</tex> в RBST, состоящее из <tex>n</tex> вершин. С вероятностью <tex dpi = "150">\frac{1}{n+1}</tex> вставим ключ в корень дерева, используя процедуру ''insert_at_root''. С вероятностью <tex dpi = "150">1 - \frac{1}{n+1} = \frac{n}{n+1}</tex> вставим его в правое поддереао, если он больше корня, или в левое поддерево, если меньше. Ниже приведён псевдокод процедуры вставки ''insert'', процедуры ''insert_at_root'', а также процедуры ''split(k)'', разбивающей дерево на два поддерева, в одном из которых все ключи строго меньше <tex>k</tex>, а в другом больше, либо равны; приведена достаточно очевидная рекурсивная реализация. (через Node обозначен тип вершины дерева, дерево представляется как указатель на корень) |
− | '''Node''' '''insert''' (x | + | '''Node''' '''insert''' (T, x) |
− | '''int''' r = '''random'''(0..size( | + | '''int''' r = '''random'''(0..size(T)) |
'''if''' (r = n) | '''if''' (r = n) | ||
− | T = | + | T = insert_at_root(T, x) |
− | '''if''' (x < root | + | '''if''' (x < root.key) |
− | T = | + | T = insert(T.left, x) |
'''else''' | '''else''' | ||
− | T = insert( | + | T = insert(T.right, x) |
'''return''' T | '''return''' T | ||
− | Заметим, что если дерево пусто, то insert с вероятностью 1 делает <tex>x</tex> корнем. | + | Заметим, что если дерево пусто, то ''insert'' с вероятностью 1 делает <tex>x</tex> корнем. |
// вставляет ключ x в дерево T | // вставляет ключ x в дерево T | ||
− | '''Node''' '''insert_at_root''' (x | + | '''Node''' '''insert_at_root''' (T, x) |
− | + | // создать пустые L и R | |
− | + | L = RBST() | |
− | + | R = RBST() | |
− | T T | + | split(T, x, L, R) |
− | T | + | // создать пустое T |
− | T | + | T = RBST() |
+ | T.key = x | ||
+ | T.left = L | ||
+ | T.left = R | ||
'''return''' T | '''return''' T | ||
// разделяет дерево T по x | // разделяет дерево T по x | ||
// результат: деревья L и R | // результат: деревья L и R | ||
− | '''split''' (x | + | '''split''' (T, x, L, R) |
− | '''if''' (T | + | '''if''' (size(T) = 0) |
− | + | // создать пустые L и R | |
− | '''else''' '''if''' (x < T | + | L = RBST() |
+ | R = RBST() | ||
+ | '''else''' '''if''' (x < T.key) | ||
R = T | R = T | ||
− | + | split (T.left, x, L, R.left) | |
'''else''' | '''else''' | ||
L = T | L = T | ||
− | + | split (T.right, x, L.right, R) | |
− | Далее рассмотрим как меняется свойство дерева быть | + | Далее рассмотрим как меняется свойство дерева быть рандомизированным при вставке в него ключей. |
{{Лемма | {{Лемма | ||
− | |statement= Пусть после операции split от дерева <tex>T</tex> по ключу <tex>x</tex> были получены деревья <tex>T_{ | + | |statement= Пусть после операции ''split'' от дерева <tex>T</tex> по ключу <tex>x</tex> были получены деревья <tex>T_{L}</tex> и <tex>T_{R}</tex>. Тогда если <tex>T</tex> было рандомизированным бинарным деревом поиска, содержащим множество ключей <tex>K</tex>, то деревья <tex>T_{L}</tex> и <tex>T_{R}</tex> {{---}} рандомизированные бинарные деревья поиска, содержащие соответственно множества ключей <tex>K_{L} = \{y \in K | y < x\}</tex> и <tex>K_{R} = \{y \in K | y > x\}</tex>. |
|proof= | |proof= | ||
Применим индукцию по <tex>n</tex> {{---}} размеру дерева. Если <tex>n = 0</tex>, то лемма верна (получим два пустых дерева). | Применим индукцию по <tex>n</tex> {{---}} размеру дерева. Если <tex>n = 0</tex>, то лемма верна (получим два пустых дерева). | ||
− | Пусть <tex>n > 0</tex>, и лемма верна при всех меньших размерах дерева.. Пусть также <tex>y = T | + | Пусть <tex>n > 0</tex>, и лемма верна при всех меньших размерах дерева.. Пусть также <tex>y = T.key, L = T.left, R = T.right</tex>. Если <tex>x > y</tex>, то <tex>y</tex> {{---}} корень <tex>T_{L}</tex>, <tex>L</tex> {{---}} левое поддерево <tex>T_{L}</tex>, а ''split'' рекурсивно вызовется от <tex>R</tex>, разделив его на <tex>R'</tex> {{---}} правое поддерево <tex>T_{L}</tex> {{---}}, и <tex>T_{R}</tex>, которые по предположению индукции будут рандомизированными бинарными деревьями поиска. Но <tex>L</tex> также является RBST, т.к. является поддеревом <tex>T</tex>. |
− | Итак для того, чтобы доказать, что <tex>T_{ | + | Итак для того, чтобы доказать, что <tex>T_{L}</tex> {{---}} рандомизированное бинарное дерево поиска, осталось показать, что любая его вершина <tex>z</tex> с вероятностью <tex dpi = "150">\frac{1}{m}</tex> окажется в корне, где <tex>m</tex> {{---}} размер <tex>T_{L}</tex>. Действительно: |
− | (пусть <tex>A | + | (пусть событие <tex>A</tex> {{---}} <tex>z</tex> является коренем <tex>T_{L}</tex>) |
<tex dpi = "150">P[A | y < x] = \frac{P[A \wedge y < x]}{P[y < x]} = \frac{1 / n}{m / n} = \frac{1}{m}</tex> | <tex dpi = "150">P[A | y < x] = \frac{P[A \wedge y < x]}{P[y < x]} = \frac{1 / n}{m / n} = \frac{1}{m}</tex> | ||
Строка 78: | Строка 83: | ||
{{Теорема | {{Теорема | ||
− | |statement= Если <tex>T</tex> {{---}} | + | |statement= Если <tex>T</tex> {{---}} рандомизированное бинарное дерево поиска, содержащее множество ключей <tex>K</tex>, <tex>x \notin K</tex>, тогда процедура ''insert(x, T)'' вернёт рандомизированное бинарное дерево поиска <tex>T</tex>, содержащее множество ключей <tex>K \cap x</tex>. |
|proof= | |proof= | ||
− | Применим индукцию по <tex>n</tex> {{---}} размеру дерева. Если <tex>n = 0</tex>, то теорема верна: после операции insert(x, T) получим дерево с корнем <tex>x</tex> и двумя пустыми поддеревьями. | + | Применим индукцию по <tex>n</tex> {{---}} размеру дерева. Если <tex>n = 0</tex>, то теорема верна: после операции ''insert(x, T)'' получим дерево с корнем <tex>x</tex> и двумя пустыми поддеревьями. |
Пусть <tex>n > 0</tex>, и теорема верна при всех меньших размерах дерева. Возможны два случая: <tex>x</tex> вставляется в корень или рекурсивно в одно из поддеревьев. | Пусть <tex>n > 0</tex>, и теорема верна при всех меньших размерах дерева. Возможны два случая: <tex>x</tex> вставляется в корень или рекурсивно в одно из поддеревьев. | ||
− | В первом случае правое и левое поддеревья <tex>x</tex> по лемме являются | + | В первом случае правое и левое поддеревья <tex>x</tex> по лемме являются рандомизированными BST, а также вероятность того, что <tex>x</tex> окажется в корне, равна <tex dpi = "150">\frac{1}{n + 1}</tex>. Т.е. новое дерево {{---}} рандомизированное BST. |
− | Во втором случае корень у дерева останется прежнем. Заметим, что для каждого <tex>y \in K</tex> вероятность быть корнем была <tex dpi = "150">\frac{1}{n}</tex>, а корнем он останется с вероятностью <tex dpi = "150">\frac{n}{n + 1}</tex>, тогда для каждого <tex>y \in K</tex> вероятность быть корнем равна <tex dpi = "150">\frac{1}{n} \cdot \frac{n}{n + 1} = \frac{1}{n + 1}</tex>. По предположению же индукции поддерево, в которое вставляется <tex>x</tex> становится | + | Во втором случае корень у дерева останется прежнем. Заметим, что для каждого <tex>y \in K</tex> вероятность быть корнем была <tex dpi = "150">\frac{1}{n}</tex>, а корнем он останется с вероятностью <tex dpi = "150">\frac{n}{n + 1}</tex>, тогда для каждого <tex>y \in K</tex> вероятность быть корнем равна <tex dpi = "150">\frac{1}{n} \cdot \frac{n}{n + 1} = \frac{1}{n + 1}</tex>. По предположению же индукции поддерево, в которое вставляется <tex>x</tex> становится рандомизированным бинарным деревом поиска; а т.к. другое поддерево корня было рандомизированным, то новое дерево {{---}} рандомизированное BST. |
}} | }} | ||
Строка 93: | Строка 98: | ||
===Удаление=== | ===Удаление=== | ||
− | Алгоритм удаления использует операцию merge {{---}} слияние двух деревьев, удовлетворяющих условию: все ключи в одном из деревьев меньше ключей во втором. Для того, чтобы удалить некоторый ключ <tex>x</tex> из RBST сначала найдём вершину с этим ключом в дереве, используя стандартный алгоритм поиска. Если вершина не найдена, то выходим из алгоритма; в противном случае сливаем правое и левое поддеревья <tex>x</tex> (заметим, что ключи в левом поддереве меньше ключей в правом), удаляем <tex>x</tex>, а корень образовавшегося дерева делаем новым сыном родителя <tex>x</tex>. Псевдокод процедур удаления и слияния приведён ниже. | + | Алгоритм удаления использует операцию ''merge'' {{---}} слияние двух деревьев, удовлетворяющих условию: все ключи в одном из деревьев меньше ключей во втором. Для того, чтобы удалить некоторый ключ <tex>x</tex> из RBST сначала найдём вершину с этим ключом в дереве, используя стандартный алгоритм поиска. Если вершина не найдена, то выходим из алгоритма; в противном случае сливаем правое и левое поддеревья <tex>x</tex> (заметим, что ключи в левом поддереве меньше ключей в правом), удаляем <tex>x</tex>, а корень образовавшегося дерева делаем новым сыном родителя <tex>x</tex>. Псевдокод процедур удаления и слияния приведён ниже. |
// удаляет ключ x из дерева T | // удаляет ключ x из дерева T | ||
− | '''Node''' '''remove'''(x | + | '''Node''' '''remove'''(T, x) |
− | '''if''' (T | + | '''if''' (size(T) = 0) |
− | + | // выйти, вернув пустое дерево | |
− | '''if''' (x < T | + | T = RBST() |
− | T | + | '''return''' T |
− | '''else''' '''if''' (x > T | + | '''if''' (x < T.key) |
− | T | + | T.left = remove(T.left, x) |
+ | '''else''' '''if''' (x > T.key) | ||
+ | T.right = remove(T.right, x) | ||
'''else''' | '''else''' | ||
− | + | // создать пустое дерево Q | |
− | Q = merge(T | + | Q = RBST() |
+ | Q = merge(T.left, T.right) | ||
T = Q | T = Q | ||
'''return''' T | '''return''' T | ||
Строка 112: | Строка 120: | ||
// результат: дерево T | // результат: дерево T | ||
'''Node''' '''merge'''(L, R) | '''Node''' '''merge'''(L, R) | ||
− | '''int''' m = L | + | '''int''' m = L.size |
− | '''int''' n = R | + | '''int''' n = R.size |
'''int''' total = m + n | '''int''' total = m + n | ||
'''if''' (total = 0) | '''if''' (total = 0) | ||
− | + | // вернуть пустое T | |
+ | T = RBST() | ||
+ | '''return''' T | ||
'''int''' r = random(1..total) | '''int''' r = random(1..total) | ||
'''if''' (r < m) | '''if''' (r < m) | ||
// с вероятностью m / (m + n) | // с вероятностью m / (m + n) | ||
− | L | + | L.right = merge(L.right, R) |
'''return''' L | '''return''' L | ||
'''if''' (r < m) | '''if''' (r < m) | ||
// с вероятностью m / (m + n) | // с вероятностью m / (m + n) | ||
− | R | + | R.left = merge(L, R.left) |
'''return''' R | '''return''' R | ||
− | Докажем, что данный алгоритм | + | Докажем, что данный алгоритм оставляет рандомизированное дерево рандомизированным. |
{{Лемма | {{Лемма | ||
− | |statement= Пусть <tex>L</tex> и <tex>R</tex> {{---}} | + | |statement= Пусть <tex>L</tex> и <tex>R</tex> {{---}} рандомизированные бинарные деревья поиска, содержащие соответственно множества ключей <tex>K_{L}</tex> и <tex>K_{R}</tex>, причём <tex>K_{L} < K_{R}</tex> (то есть каждый элемент <tex>K_{L}</tex> меньше каждого элемента <tex>K_{R}</tex>). Тогда операция ''merge(L, R)'' вернёт рандомизированное бинарное дерево поиска, содержащее множество ключей <tex>K</tex> = <tex>K_{L} \cap K_{R}</tex>. |
|proof= | |proof= | ||
Пусть <tex>m</tex> и <tex>n</tex> {{---}} размеры <tex>L</tex> и <tex>R</tex> соответственно. Применим индукцию по <tex>m</tex> и <tex>n</tex>. Если <tex>m = 0</tex> или <tex>n = 0</tex>, то лемма верна. | Пусть <tex>m</tex> и <tex>n</tex> {{---}} размеры <tex>L</tex> и <tex>R</tex> соответственно. Применим индукцию по <tex>m</tex> и <tex>n</tex>. Если <tex>m = 0</tex> или <tex>n = 0</tex>, то лемма верна. | ||
− | Пусть <tex>m > 0</tex> и <tex>n > 0</tex>, пусть также <tex>L | + | Пусть <tex>m > 0</tex> и <tex>n > 0</tex>, пусть также <tex>L.key = a</tex> или <tex>L.key = b</tex>. Без потери общности делаем корнем <tex>a</tex>. После рекурсивного слияния правого поддерева <tex>L</tex> и <tex>R</tex> получим рандомизированное бинарное дерево поиска (которое является правым поддеревом нового дерева). Левое же поддерево нового дерева тоже рандомизированное. Также верно, что для любого <tex>x \in K_{L}</tex> вероятность быть корнем равна <tex dpi = "150">\frac{1}{m + n}</tex>: действительно, вероятность оказаться в корне в <tex>L</tex> до слияния равна <tex dpi = "150">\frac{1}{m}</tex>, вероятность того, что элемент останется корнем после слияния равна <tex dpi = "150">\frac{m}{m + n}</tex>; осталось применить правило умножения. |
}} | }} | ||
{{Теорема | {{Теорема | ||
− | |statement= Если <tex>T</tex> {{---}} | + | |statement= Если <tex>T</tex> {{---}} рандомизированное бинарное дерево поиска, содержащее множество ключей <tex>K</tex>, тогда процедура ''remove(x, T)'' вернёт рандомизированное бинарное дерево поиска <tex>T'</tex>, содержащее множество ключей <tex>K \backslash \{x\}</tex> |
|proof= | |proof= | ||
Если удаляемый элемент отсутствует в дереве, то теорема верна. | Если удаляемый элемент отсутствует в дереве, то теорема верна. | ||
Строка 144: | Строка 154: | ||
Пусть <tex>x \in T</tex> (дерево не пусто), <tex>n</tex> {{---}} размер <tex>T</tex>. Докажем теорему по индукции по <tex>n</tex>. Для <tex>n = 1</tex> теорема очевидным образом верна. Пусть <tex>n > 1</tex>, и предположим, что теорема верна для всех деревьев размера меньше <tex>n</tex>. | Пусть <tex>x \in T</tex> (дерево не пусто), <tex>n</tex> {{---}} размер <tex>T</tex>. Докажем теорему по индукции по <tex>n</tex>. Для <tex>n = 1</tex> теорема очевидным образом верна. Пусть <tex>n > 1</tex>, и предположим, что теорема верна для всех деревьев размера меньше <tex>n</tex>. | ||
− | Возможно два случая: если <tex>x</tex> {{---}} корень <tex>T</tex>, то по лемме, после удаления получим | + | Возможно два случая: если <tex>x</tex> {{---}} корень <tex>T</tex>, то по лемме, после удаления получим рандомизированное бинарное дерево поиска; если же <tex>x</tex> {{---}} не корень <tex>T</tex>, то <tex>x</tex> рекурсивно удаляется из поддерева исходного дерева, и по предположению индукции после удаления получаем рандомизированное BST. Осталось лишь показать, что для любого <tex>y \in T, y \neq x</tex> вероятность оказаться корнем после удаления равна <tex dpi = "150">\frac{1}{n - 1}</tex>. |
Введём обозначения: | Введём обозначения: | ||
− | <tex>A | + | событие <tex>A</tex> {{---}} <tex>y</tex> является коренем <tex>T'</tex>; |
− | <tex>B | + | событие <tex>B</tex> {{---}} <tex>x</tex> был корнем <tex>T</tex> (до операции ''remove''); |
− | <tex>C | + | событие <tex>C</tex> {{---}} <tex>y</tex> стал корнем <tex>T'</tex> после операции ''merge'' (но до этого им не являлся); |
− | <tex>D | + | событие <tex>D</tex> {{---}} <tex>y</tex> был корнем <tex>T</tex> (до операции ''remove''); |
Тогда: | Тогда: | ||
Строка 164: | Строка 174: | ||
==Анализ времени работы== | ==Анализ времени работы== | ||
− | Достаточно очевидно, что время работы приведённых алгоритмов пропорционально глубине дерева. Но т.к. математическое ожидание глубины | + | Достаточно очевидно, что время работы приведённых алгоритмов пропорционально глубине дерева. Но т.к. математическое ожидание глубины рандомизированного бинарного дерева поиска есть <tex>O (\log n)</tex>, где <tex>n</tex> {{---}} число вершин в дереве, то математическое ожидание времени работы поиска, вставки и удаления {{---}} также <tex>O (\log n)</tex>. |
− | == | + | ==См. также== |
*[[Дерево поиска, наивная реализация]] | *[[Дерево поиска, наивная реализация]] | ||
*[[Декартово дерево]] | *[[Декартово дерево]] | ||
Строка 176: | Строка 186: | ||
== Литература == | == Литература == | ||
− | * | + | * Martinez, Conrado; Roura, Salvador (1997), "Randomized binary search trees", Journal of the ACM 45 |
* Seidel, Raimund; Aragon, Cecilia R. [http://people.ischool.berkeley.edu/~aragon/pubs/rst96.pdf «Randomized Search Trees»], 1996 г. | * Seidel, Raimund; Aragon, Cecilia R. [http://people.ischool.berkeley.edu/~aragon/pubs/rst96.pdf «Randomized Search Trees»], 1996 г. | ||
− | * [http://www.cs.uiuc.edu/class/sp09/cs473/notes/08-treaps.pdf Randomized binary search trees]. Lecture notes from a course by Jeff Erickson at UIUC | + | * [http://www.cs.uiuc.edu/class/sp09/cs473/notes/08-treaps.pdf Randomized binary search trees]. Lecture notes from a course by Jeff Erickson at UIUC. |
[[Категория: Дискретная математика и алгоритмы]] | [[Категория: Дискретная математика и алгоритмы]] | ||
[[Категория: Деревья поиска]] | [[Категория: Деревья поиска]] |
Версия 17:20, 1 мая 2012
Рандомизированное бинарное дерево поиска (англ. Randomized binary search tree, RBST) — структура данных, представляющая собой бинарное дерево поиска.
Содержание
Основная идея и связанные определения
Как известно, можно подобрать такую последовательность операций с бинарным деревом поиска в наивной реализации, что его глубина будет пропорциональна количеству ключей, а следовательно запрос будет выполняться за . Поэтому, если поддерживать инвариант "случайности" в дереве, то можно добиться того, что математическое ожидание глубины дерева будет небольшим. Дадим рекурсивное определение рандомизированного бинарного дерева поиска (RBST).
Определение: |
Пусть
| — бинарное дерево поиска. Тогда
Из определения следует, что каждый ключ в RBST размера n может оказаться корнем с вероятностью 1/n.
Идея RBST состоит в том, что хранимое дерево постоянно является рандомизированным бинарным деревом поиска. Далее подробно будет описана реализация операций над RBST, которая позволит добиться этой цели. Заметим лишь, что хранение RBST в памяти ничем не отличается от хранения обычного дерева поиска: хранится указатель на корень; в каждой вершине хранятся указатели на её сыновей.
(Похожие идеи используются в декартовом дереве, поэтому во многих русскоязычных ресурсах термин рандомизированное бинарное дерево поиска используется как синонимическое название декартового дерева и декартового дерева по неявному ключу)
Операции
Операции обхода дерева, поиска ключа, поиска максимума/минимума, поиск следующего/предыдущего элемента выполняются как в обычном дереве поиска, т.к. не меняют структуру дерева.
Вставка
Рассмотрим рекурсивный алгоритм вставки ключа
в RBST, состоящее из вершин. С вероятностью вставим ключ в корень дерева, используя процедуру insert_at_root. С вероятностью вставим его в правое поддереао, если он больше корня, или в левое поддерево, если меньше. Ниже приведён псевдокод процедуры вставки insert, процедуры insert_at_root, а также процедуры split(k), разбивающей дерево на два поддерева, в одном из которых все ключи строго меньше , а в другом больше, либо равны; приведена достаточно очевидная рекурсивная реализация. (через Node обозначен тип вершины дерева, дерево представляется как указатель на корень)Node insert (T, x) int r = random(0..size(T)) if (r = n) T = insert_at_root(T, x) if (x < root.key) T = insert(T.left, x) else T = insert(T.right, x) return T
Заметим, что если дерево пусто, то insert с вероятностью 1 делает
корнем.// вставляет ключ x в дерево T Node insert_at_root (T, x) // создать пустые L и R L = RBST() R = RBST() split(T, x, L, R) // создать пустое T T = RBST() T.key = x T.left = L T.left = R return T
// разделяет дерево T по x // результат: деревья L и R split (T, x, L, R) if (size(T) = 0) // создать пустые L и R L = RBST() R = RBST() else if (x < T.key) R = T split (T.left, x, L, R.left) else L = T split (T.right, x, L.right, R)
Далее рассмотрим как меняется свойство дерева быть рандомизированным при вставке в него ключей.
Лемма: |
Пусть после операции split от дерева по ключу были получены деревья и . Тогда если было рандомизированным бинарным деревом поиска, содержащим множество ключей , то деревья и — рандомизированные бинарные деревья поиска, содержащие соответственно множества ключей и . |
Доказательство: |
Применим индукцию по — размеру дерева. Если , то лемма верна (получим два пустых дерева).Пусть , и лемма верна при всех меньших размерах дерева.. Пусть также . Если , то — корень , — левое поддерево , а split рекурсивно вызовется от , разделив его на — правое поддерево —, и , которые по предположению индукции будут рандомизированными бинарными деревьями поиска. Но также является RBST, т.к. является поддеревом .Итак для того, чтобы доказать, что — рандомизированное бинарное дерево поиска, осталось показать, что любая его вершина с вероятностью окажется в корне, где — размер . Действительно:(пусть событие — является коренем )Случай, когда симметричен рассмотренному. |
Теорема: |
Если — рандомизированное бинарное дерево поиска, содержащее множество ключей , , тогда процедура insert(x, T) вернёт рандомизированное бинарное дерево поиска , содержащее множество ключей . |
Доказательство: |
Применим индукцию по — размеру дерева. Если , то теорема верна: после операции insert(x, T) получим дерево с корнем и двумя пустыми поддеревьями.Пусть , и теорема верна при всех меньших размерах дерева. Возможны два случая: вставляется в корень или рекурсивно в одно из поддеревьев.В первом случае правое и левое поддеревья Во втором случае корень у дерева останется прежнем. Заметим, что для каждого по лемме являются рандомизированными BST, а также вероятность того, что окажется в корне, равна . Т.е. новое дерево — рандомизированное BST. вероятность быть корнем была , а корнем он останется с вероятностью , тогда для каждого вероятность быть корнем равна . По предположению же индукции поддерево, в которое вставляется становится рандомизированным бинарным деревом поиска; а т.к. другое поддерево корня было рандомизированным, то новое дерево — рандомизированное BST. |
Пусть
— множество ключей, — какая-то фиксированная перестановка элементов . Из приведённой выше теоремы следует, что если в изначально пустое дерево добавлять ключи P по порядку, то получим дерево , являющееся RBST.Удаление
Алгоритм удаления использует операцию merge — слияние двух деревьев, удовлетворяющих условию: все ключи в одном из деревьев меньше ключей во втором. Для того, чтобы удалить некоторый ключ
из RBST сначала найдём вершину с этим ключом в дереве, используя стандартный алгоритм поиска. Если вершина не найдена, то выходим из алгоритма; в противном случае сливаем правое и левое поддеревья (заметим, что ключи в левом поддереве меньше ключей в правом), удаляем , а корень образовавшегося дерева делаем новым сыном родителя . Псевдокод процедур удаления и слияния приведён ниже.// удаляет ключ x из дерева T Node remove(T, x) if (size(T) = 0) // выйти, вернув пустое дерево T = RBST() return T if (x < T.key) T.left = remove(T.left, x) else if (x > T.key) T.right = remove(T.right, x) else // создать пустое дерево Q Q = RBST() Q = merge(T.left, T.right) T = Q return T
// сливает деревья L и R // результат: дерево T Node merge(L, R) int m = L.size int n = R.size int total = m + n if (total = 0) // вернуть пустое T T = RBST() return T int r = random(1..total) if (r < m) // с вероятностью m / (m + n) L.right = merge(L.right, R) return L if (r < m) // с вероятностью m / (m + n) R.left = merge(L, R.left) return R
Докажем, что данный алгоритм оставляет рандомизированное дерево рандомизированным.
Лемма: |
Пусть и — рандомизированные бинарные деревья поиска, содержащие соответственно множества ключей и , причём (то есть каждый элемент меньше каждого элемента ). Тогда операция merge(L, R) вернёт рандомизированное бинарное дерево поиска, содержащее множество ключей = . |
Доказательство: |
Пусть Пусть и — размеры и соответственно. Применим индукцию по и . Если или , то лемма верна. и , пусть также или . Без потери общности делаем корнем . После рекурсивного слияния правого поддерева и получим рандомизированное бинарное дерево поиска (которое является правым поддеревом нового дерева). Левое же поддерево нового дерева тоже рандомизированное. Также верно, что для любого вероятность быть корнем равна : действительно, вероятность оказаться в корне в до слияния равна , вероятность того, что элемент останется корнем после слияния равна ; осталось применить правило умножения. |
Теорема: |
Если — рандомизированное бинарное дерево поиска, содержащее множество ключей , тогда процедура remove(x, T) вернёт рандомизированное бинарное дерево поиска , содержащее множество ключей |
Доказательство: |
Если удаляемый элемент отсутствует в дереве, то теорема верна. Пусть (дерево не пусто), — размер . Докажем теорему по индукции по . Для теорема очевидным образом верна. Пусть , и предположим, что теорема верна для всех деревьев размера меньше .Возможно два случая: если — корень , то по лемме, после удаления получим рандомизированное бинарное дерево поиска; если же — не корень , то рекурсивно удаляется из поддерева исходного дерева, и по предположению индукции после удаления получаем рандомизированное BST. Осталось лишь показать, что для любого вероятность оказаться корнем после удаления равна .Введём обозначения: событие — является коренем ;событие — был корнем (до операции remove);событие — стал корнем после операции merge (но до этого им не являлся);событие — был корнем (до операции remove);Тогда: . |
Анализ времени работы
Достаточно очевидно, что время работы приведённых алгоритмов пропорционально глубине дерева. Но т.к. математическое ожидание глубины рандомизированного бинарного дерева поиска есть
, где — число вершин в дереве, то математическое ожидание времени работы поиска, вставки и удаления — также .См. также
Ссылки
Литература
- Martinez, Conrado; Roura, Salvador (1997), "Randomized binary search trees", Journal of the ACM 45
- Seidel, Raimund; Aragon, Cecilia R. «Randomized Search Trees», 1996 г.
- Randomized binary search trees. Lecture notes from a course by Jeff Erickson at UIUC.