Декартово дерево — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Реализация №2:)
Строка 1: Строка 1:
 
''Эта статья про Курево''
 
''Эта статья про Курево''
  
'''Декартово дерево''' {{---}} это структура данных, объединяющая в себе бинарное дерево поиска и бинарную кучу (отсюда и второе её название: <tex>treap (tree+heap)</tex> и дерамида (дерево+пирамида), так же существует название курево (куча + дерево).
+
== Описание ==
 +
'''Декартово дерево''' {{---}} это структура данных, объединяющая в себе бинарное дерево поиска и бинарную кучу (отсюда и второе её название: treap (tree + heap) и дерамида (дерево + пирамида), так же существует название курево (куча + дерево).
  
 
Более строго, это структура данных, которая хранит пары <tex> (X,Y) </tex> в виде бинарного дерева таким образом, что она является бинарным деревом поиска по <tex>x</tex> и бинарной пирамидой по <tex>y</tex>. Предполагая, что все <tex>X</tex> и все <tex>Y</tex> являются различными, получаем, что если некоторый элемент дерева содержит <tex>(X_0,Y_0)</tex>, то у всех элементов в левом поддереве <tex>X < X_0</tex>, у всех элементов в правом поддереве <tex> X > X_0</tex>, а также и в левом, и в правом поддереве имеем: <tex> Y < Y_0</tex>.
 
Более строго, это структура данных, которая хранит пары <tex> (X,Y) </tex> в виде бинарного дерева таким образом, что она является бинарным деревом поиска по <tex>x</tex> и бинарной пирамидой по <tex>y</tex>. Предполагая, что все <tex>X</tex> и все <tex>Y</tex> являются различными, получаем, что если некоторый элемент дерева содержит <tex>(X_0,Y_0)</tex>, то у всех элементов в левом поддереве <tex>X < X_0</tex>, у всех элементов в правом поддереве <tex> X > X_0</tex>, а также и в левом, и в правом поддереве имеем: <tex> Y < Y_0</tex>.
Строка 7: Строка 8:
 
Дерамиды были предложены Сиделем (Siedel) и Арагоном (Aragon) в 1996 г.
 
Дерамиды были предложены Сиделем (Siedel) и Арагоном (Aragon) в 1996 г.
  
== Операция split ==
+
== Операции в декартовом дереве ==
 +
=== Split ===
 
[[file:split.png|thumb|400px|Операция split]]
 
[[file:split.png|thumb|400px|Операция split]]
  
Операция <tex>\mathrm{split}</tex> (''разрезать'') позволяет сделать следующее: разрезать декартово дерево <tex>T</tex> по ключу  
+
Операция <tex>\mathrm{Split}</tex> (''разрезать'') позволяет сделать следующее: разрезать декартово дерево <tex>T</tex> по ключу  
 
<tex>x</tex> и получить два других декартовых дерева: <tex>T_1</tex> и <tex>T_2</tex>, причем в <tex>T_1</tex>
 
<tex>x</tex> и получить два других декартовых дерева: <tex>T_1</tex> и <tex>T_2</tex>, причем в <tex>T_1</tex>
 
находятся все ключи дерева <tex>T</tex>, не большие <tex>x</tex>, а в <tex>T_2</tex> {{---}} большие <tex>x</tex>.
 
находятся все ключи дерева <tex>T</tex>, не большие <tex>x</tex>, а в <tex>T_2</tex> {{---}} большие <tex>x</tex>.
  
<tex>\mathrm{split}(T, x) \to \{T_1, T_2\}</tex>.  
+
<tex>\mathrm{Split}(T, x) \to \{T_1, T_2\}</tex>.  
  
 
Эта операция устроена следующим образом.
 
Эта операция устроена следующим образом.
Строка 25: Строка 27:
 
Случай, в котором требуется разрезать дерево по ключу, меньше либо равному ключа в корне, рассматривается симметрично.
 
Случай, в котором требуется разрезать дерево по ключу, меньше либо равному ключа в корне, рассматривается симметрично.
  
Оценим время работы операции <tex>\mathrm{split}</tex>. Во время выполнения вызывается одна операция <tex>\mathrm{split}</tex> для
+
Оценим время работы операции <tex>\mathrm{Split}</tex>. Во время выполнения вызывается одна операция <tex>\mathrm{Split}</tex> для
 
дерева хотя бы на один меньшей высоты и делается ещё <tex>\mathcal{O}(1)</tex> операция. Тогда итоговая трудоёмкость этой операции
 
дерева хотя бы на один меньшей высоты и делается ещё <tex>\mathcal{O}(1)</tex> операция. Тогда итоговая трудоёмкость этой операции
равна <tex>\mathcal{O}(h)</tex>, где <tex>h</tex> {{---}} высота дерева. Так как высота декартова дерева {{---}}
+
равна <tex>\mathcal{O}(h)</tex>, где <tex>h</tex> {{---}} высота дерева.
<tex>\mathcal{O}(\log n)</tex>, то и операция <tex>\mathrm{split}</tex> работает за <tex>\mathcal{O}(\log n)</tex>.
 
  
== Операция merge ==
+
=== Merge ===
 
[[file:merge.png|thumb|400px|Операция merge]]
 
[[file:merge.png|thumb|400px|Операция merge]]
  
Рассмотрим вторую операцию с декартовыми деревьями {{---}} <tex>\mathrm{merge}</tex>(''слить'').  
+
Рассмотрим вторую операцию с декартовыми деревьями {{---}} <tex>\mathrm{Merge}</tex>(''слить'').  
  
 
С помощью этой операции можно слить два декартовых дерева в одно.
 
С помощью этой операции можно слить два декартовых дерева в одно.
Строка 39: Строка 40:
 
ключи во втором(''правом''). В результате получается дерево, в котором есть все ключи из первого и второго деревьев.
 
ключи во втором(''правом''). В результате получается дерево, в котором есть все ключи из первого и второго деревьев.
  
<tex>\mathrm{merge}(T_1, T_2) \to T</tex>
+
<tex>\mathrm{Merge}(T_1, T_2) \to T</tex>
  
 
Рассмотрим принцип работы этой операции. Пусть нужно слить деревья <tex>T_1</tex> и <tex>T_2</tex>.
 
Рассмотрим принцип работы этой операции. Пусть нужно слить деревья <tex>T_1</tex> и <tex>T_2</tex>.
Строка 52: Строка 53:
 
<tex>T_1</tex> и дерева <tex>T_2</tex>.
 
<tex>T_1</tex> и дерева <tex>T_2</tex>.
  
Рассуждая аналогично операции <tex>\mathrm{split}</tex> приходим к выводу, что трудоёмкость операции <tex>\mathrm{merge}</tex>  
+
Рассуждая аналогично операции <tex>\mathrm{Split}</tex> приходим к выводу, что трудоёмкость операции <tex>\mathrm{Merge}</tex>  
равна <tex>\mathcal{O}(\log n)</tex>.
+
равна <tex>\mathcal{O}(h)</tex>, где <tex>h</tex> {{---}} высота дерева.
  
== Операция add ==
+
=== Insert ===
Операция <tex>\mathrm{add}(T, k)</tex> добавляет в дерево <tex>T</tex> элемент <tex>k</tex>, где <tex>k.x</tex> {{---}} ключ, а <tex>k.y</tex>{{---}} приоритет.
+
Операция <tex>\mathrm{Insert}(T, k)</tex> добавляет в дерево <tex>T</tex> элемент <tex>k</tex>, где <tex>k.x</tex> {{---}} ключ, а <tex>k.y</tex>{{---}} приоритет.
  
===Реализация №1:===
+
==== Реализация №1 ====
# Разобьём наше дерево по ключу, который мы хотим добавить, то есть <tex>\mathrm{split}(T, k.x) \to \{T_1, T_2\}</tex>.
+
# Разобьём наше дерево по ключу, который мы хотим добавить, то есть <tex>\mathrm{Split}(T, k.x) \to \{T_1, T_2\}</tex>.
# Сливаем первое дерево с новым элементом, то есть <tex>\mathrm{merge}(T_1, k) \to T_1</tex>.
+
# Сливаем первое дерево с новым элементом, то есть <tex>\mathrm{Merge}(T_1, k) \to T_1</tex>.
# Сливаем получившиеся дерево со вторым, то есть <tex>\mathrm{merge}(T_1, T_2) \to T</tex>.
+
# Сливаем получившиеся дерево со вторым, то есть <tex>\mathrm{Merge}(T_1, T_2) \to T</tex>.
  
 +
==== Реализация №2  ====
 +
# Сначала спускаемся по дереву (как в обычном бинарном дереве поиска по <tex>k.x</tex>), но останавливаемся на первом элементе, в котором значение приоритета оказалось меньше <tex>k.y</tex>.
 +
# Теперь вызываем <tex>\mathrm{Split }(T, k.x) \to \{T_1, T_2\}</tex> от найденного элемента (от элемента вместе со всем его поддеревом)
 +
# Полученные <tex>T_1</tex> и <tex>T_2</tex> записываем в качестве левого и правого сына добавляемого элемента.
 +
# Полученное дерево подвешиваем на место элемента, найденного в первом пункте.
  
=== Реализация №2:  ===
+
=== Remove ===
Сначала спускаемся по дереву (как в обычном бинарном дереве поиска по <tex>k.x</tex>), но останавливаемся на первом элементе, в котором значение приоритета оказалось меньше <tex>k.y</tex>. Мы нашли позицию, куда будем вставлять наш элемент. Теперь вызываем <tex>\mathrm{split }(T, k.x) \to \{T_1, T_2\}</tex> от найденного элемента (от элемента вместе со всем его поддеревом), и возвращаемые ею <tex>T_1</tex> и <tex>T_2</tex> записываем в качестве левого и правого сына добавляемого элемента.
+
Операция <tex>\mathrm{Remove}(T, x)</tex> удаляет из дерева <tex>T</tex> элемент с ключом <tex>x</tex>.
  
 +
==== Реализация №1 ====
  
== Операция remove ==
+
# Разобьём наше дерево по ключу, который мы хотим удалить, то есть <tex>\mathrm{Split }(T, k.x) \to \{T_1, T_2\}</tex>.
Операция <tex>\mathrm{remove}(T, x)</tex> удаляет из дерева <tex>T</tex> элемент с ключом <tex>x</tex>.
+
# Теперь отделяем от первого дерева элемент <tex>x</tex>, опять таки разбивая по ключу <tex>x</tex>, то есть <tex>\mathrm{Split }(T_1, k.x - \varepsilon) \to \{T_1, T_3\}</tex>.
 +
# Сливаем первое дерево со вторым, то есть <tex>\mathrm{Merge }(T_1, T_2) \to T</tex>.  
  
===Реализация №1:===
+
====Реализация №2====
 +
# Спускаемся по дереву (как в обычном бинарном дереве поиска по <tex>x</tex>), ища удаляемый элемент.
 +
# Найдя элемент, вызываем <tex>Merge</tex> его левого и правого сыновей, и возвращаемое ею значение ставим на место удаляемого элемента, то есть <tex>\mathrm{Merge }(T.l, T.t) \to T</tex>.
  
# Разобьём наше дерево по ключу, который мы хотим удалить, то есть <tex>\mathrm{split }(T, k.x) \to \{T_1, T_2\}</tex>.
+
== Высота декартового дерева ==
# Теперь отделяем от первого дерева элемент <tex>x</tex>, опять таки разбивая по ключу <tex>x</tex>, то есть <tex>\mathrm{split }(T_1, k.x - \varepsilon) \to \{T_1, T_3\}</tex>.
+
Мы уже выяснили, что сложность операций с декартовым деревом линейно зависит от его высоты. В действительности высота декартова дерева может быть линейной относительно его размеров. Например, высота декартова дерева, построенного по набору ключей <tex>(1, 1), \ldots, (n, n)</tex>, будет равна <tex>n</tex>. Во избежание таких случаев, полезным оказывается выбирать приоритеты в ключах случайно.
# Сливаем первое дерево со вторым, то есть <tex>\mathrm{merge }(T_1, T_2) \to T</tex>.
 
  
===Реализация №2:===
+
== Ссылки ==
Спускаемся по дереву (как в обычном бинарном дереве поиска по <tex>x</tex>), ища удаляемый элемент. Найдя элемент, мы просто вызываем <tex>merge</tex> его левого и правого сыновей, и возвращаемое ею значение ставим на место удаляемого элемента, то есть <tex>\mathrm{merge }(T.l, T.t) \to T</tex>.
 
 
 
== Литература ==
 
 
Todo
 
Todo
  
 
[[Категория:Дискретная математика и алгоритмы]]
 
[[Категория:Дискретная математика и алгоритмы]]
 
[[Категория:Деревья поиска]]
 
[[Категория:Деревья поиска]]

Версия 18:39, 11 апреля 2012

Эта статья про Курево

Описание

Декартово дерево — это структура данных, объединяющая в себе бинарное дерево поиска и бинарную кучу (отсюда и второе её название: treap (tree + heap) и дерамида (дерево + пирамида), так же существует название курево (куча + дерево).

Более строго, это структура данных, которая хранит пары [math] (X,Y) [/math] в виде бинарного дерева таким образом, что она является бинарным деревом поиска по [math]x[/math] и бинарной пирамидой по [math]y[/math]. Предполагая, что все [math]X[/math] и все [math]Y[/math] являются различными, получаем, что если некоторый элемент дерева содержит [math](X_0,Y_0)[/math], то у всех элементов в левом поддереве [math]X \lt X_0[/math], у всех элементов в правом поддереве [math] X \gt X_0[/math], а также и в левом, и в правом поддереве имеем: [math] Y \lt Y_0[/math].

Дерамиды были предложены Сиделем (Siedel) и Арагоном (Aragon) в 1996 г.

Операции в декартовом дереве

Split

Операция split

Операция [math]\mathrm{Split}[/math] (разрезать) позволяет сделать следующее: разрезать декартово дерево [math]T[/math] по ключу [math]x[/math] и получить два других декартовых дерева: [math]T_1[/math] и [math]T_2[/math], причем в [math]T_1[/math] находятся все ключи дерева [math]T[/math], не большие [math]x[/math], а в [math]T_2[/math] — большие [math]x[/math].

[math]\mathrm{Split}(T, x) \to \{T_1, T_2\}[/math].

Эта операция устроена следующим образом.

Рассмотрим случай, в котором требуется разрезать дерево по ключу, большему ключа корня. Посмотрим, как будут устроены результирующие деревья [math]T_1[/math] и [math]T_2[/math]:

  • [math]T_1[/math]: левое поддерево [math]T_1[/math] совпадёт с левым поддеревом [math]T[/math]. Для нахождения правого поддерева [math]T_1[/math], нужно разрезать правое поддерево [math]T[/math] на [math]T^R_1[/math] и [math]T^R_2[/math] по ключу [math]x[/math] и взять [math]T^R_1[/math].
  • [math]T_2[/math] совпадёт с [math]T^R_2[/math].

Случай, в котором требуется разрезать дерево по ключу, меньше либо равному ключа в корне, рассматривается симметрично.

Оценим время работы операции [math]\mathrm{Split}[/math]. Во время выполнения вызывается одна операция [math]\mathrm{Split}[/math] для дерева хотя бы на один меньшей высоты и делается ещё [math]\mathcal{O}(1)[/math] операция. Тогда итоговая трудоёмкость этой операции равна [math]\mathcal{O}(h)[/math], где [math]h[/math] — высота дерева.

Merge

Операция merge

Рассмотрим вторую операцию с декартовыми деревьями — [math]\mathrm{Merge}[/math](слить).

С помощью этой операции можно слить два декартовых дерева в одно. Причем, все ключи в первом(левом) дереве должны быть меньше, чем ключи во втором(правом). В результате получается дерево, в котором есть все ключи из первого и второго деревьев.

[math]\mathrm{Merge}(T_1, T_2) \to T[/math]

Рассмотрим принцип работы этой операции. Пусть нужно слить деревья [math]T_1[/math] и [math]T_2[/math]. Тогда, очевидно, у результирующего дерева [math]T[/math] есть корень. Корнем станет вершина из [math]T_1[/math] или [math]T_2[/math] с наибольшим ключом [math]y[/math]. Но вершина с самым большим [math]y[/math] из всех вершин деревьев [math]T_1[/math] и [math]T_2[/math] может быть только либо корнем [math]T_1[/math], либо корнем [math]T_2[/math]. Рассмотрим случай, в котором корень [math]T_1[/math] имеет больший [math]y[/math], чем корень [math]T_2[/math]. Случай, в котором корень [math]T_2[/math] имеет больший [math]y[/math], чем корень [math]T_1[/math], симметричен этому.

Если [math]y[/math] корня [math]T_1[/math] больше [math]y[/math] корня [math]T_2[/math], то он и будет являться корнем. Тогда левое поддерево [math]T[/math] совпадёт с левым поддеревом [math]T_1[/math]. Справа же нужно подвесить объединение правого поддерева [math]T_1[/math] и дерева [math]T_2[/math].

Рассуждая аналогично операции [math]\mathrm{Split}[/math] приходим к выводу, что трудоёмкость операции [math]\mathrm{Merge}[/math] равна [math]\mathcal{O}(h)[/math], где [math]h[/math] — высота дерева.

Insert

Операция [math]\mathrm{Insert}(T, k)[/math] добавляет в дерево [math]T[/math] элемент [math]k[/math], где [math]k.x[/math] — ключ, а [math]k.y[/math]— приоритет.

Реализация №1

  1. Разобьём наше дерево по ключу, который мы хотим добавить, то есть [math]\mathrm{Split}(T, k.x) \to \{T_1, T_2\}[/math].
  2. Сливаем первое дерево с новым элементом, то есть [math]\mathrm{Merge}(T_1, k) \to T_1[/math].
  3. Сливаем получившиеся дерево со вторым, то есть [math]\mathrm{Merge}(T_1, T_2) \to T[/math].

Реализация №2

  1. Сначала спускаемся по дереву (как в обычном бинарном дереве поиска по [math]k.x[/math]), но останавливаемся на первом элементе, в котором значение приоритета оказалось меньше [math]k.y[/math].
  2. Теперь вызываем [math]\mathrm{Split }(T, k.x) \to \{T_1, T_2\}[/math] от найденного элемента (от элемента вместе со всем его поддеревом)
  3. Полученные [math]T_1[/math] и [math]T_2[/math] записываем в качестве левого и правого сына добавляемого элемента.
  4. Полученное дерево подвешиваем на место элемента, найденного в первом пункте.

Remove

Операция [math]\mathrm{Remove}(T, x)[/math] удаляет из дерева [math]T[/math] элемент с ключом [math]x[/math].

Реализация №1

  1. Разобьём наше дерево по ключу, который мы хотим удалить, то есть [math]\mathrm{Split }(T, k.x) \to \{T_1, T_2\}[/math].
  2. Теперь отделяем от первого дерева элемент [math]x[/math], опять таки разбивая по ключу [math]x[/math], то есть [math]\mathrm{Split }(T_1, k.x - \varepsilon) \to \{T_1, T_3\}[/math].
  3. Сливаем первое дерево со вторым, то есть [math]\mathrm{Merge }(T_1, T_2) \to T[/math].

Реализация №2

  1. Спускаемся по дереву (как в обычном бинарном дереве поиска по [math]x[/math]), ища удаляемый элемент.
  2. Найдя элемент, вызываем [math]Merge[/math] его левого и правого сыновей, и возвращаемое ею значение ставим на место удаляемого элемента, то есть [math]\mathrm{Merge }(T.l, T.t) \to T[/math].

Высота декартового дерева

Мы уже выяснили, что сложность операций с декартовым деревом линейно зависит от его высоты. В действительности высота декартова дерева может быть линейной относительно его размеров. Например, высота декартова дерева, построенного по набору ключей [math](1, 1), \ldots, (n, n)[/math], будет равна [math]n[/math]. Во избежание таких случаев, полезным оказывается выбирать приоритеты в ключах случайно.

Ссылки

Todo