Декартово дерево

Материал из Викиконспекты
Версия от 14:06, 11 апреля 2012; 89.163.5.56 (обсуждение) (Реализация №1:)
Перейти к: навигация, поиск

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

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

Более строго, это структура данных, которая хранит пары (X,Y) в виде бинарного дерева таким образом, что она является бинарным деревом поиска по x и бинарной пирамидой по y. Предполагая, что все X и все Y являются различными, получаем, что если некоторый элемент дерева содержит (X0,Y0), то у всех элементов в левом поддереве X<X0, у всех элементов в правом поддереве X>X0, а также и в левом, и в правом поддереве имеем: Y<Y0.

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

Операция split

Операция split

Операция split (разрезать) позволяет сделать следующее: разрезать декартово дерево T по ключу x и получить два других декартовых дерева: T1 и T2, причем в T1 находятся все ключи дерева T, не большие x, а в T2 — большие x.

split(T,x){T1,T2}.

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

Рассмотрим случай, в котором требуется разрезать дерево по ключу, большему ключа корня. Посмотрим, как будут устроены результирующие деревья T1 и T2:

  • T1: левое поддерево T1 совпадёт с левым поддеревом T. Для нахождения правого поддерева T1, нужно разрезать правое поддерево T на TR1 и TR2 по ключу x и взять TR1.
  • T2 совпадёт с TR2.

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

Оценим время работы операции split. Во время выполнения вызывается одна операция split для дерева хотя бы на один меньшей высоты и делается ещё O(1) операция. Тогда итоговая трудоёмкость этой операции равна O(h), где h — высота дерева. Так как высота декартова дерева — O(logn), то и операция split работает за O(logn).

Операция merge

Операция merge

Рассмотрим вторую операцию с декартовыми деревьями — merge(слить).

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

merge(T1,T2)T

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

Если y корня T1 больше y корня T2, то он и будет являться корнем. Тогда левое поддерево T совпадёт с левым поддеревом T1. Справа же нужно подвесить объединение правого поддерева T1 и дерева T2.

Рассуждая аналогично операции split приходим к выводу, что трудоёмкость операции merge равна O(logn).

Операция add

Операция add(T,k) добавляет в дерево T элемент k, где k.x — ключ, а k.y— приоритет.

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

  1. Разобьём наше дерево по ключу, который мы хотим добавить, то есть split(T,k.x){T1,T2}.
  2. Сливаем первое дерево с новым элементом, то есть merge(T1,k)T1.
  3. Сливаем получившиеся дерево со вторым, то есть merge(T1,T2)T.


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

Сначала спускаемся по дереву (как в обычном бинарном дереве поиска по k.x), но останавливаемся на первом элементе, в котором значение приоритета оказалось меньше k.y. Мы нашли позицию, куда будем вставлять наш элемент. Теперь вызываем split(T,k.x){T1,T2} от найденного элемента (от элемента вместе со всем его поддеревом), и возвращаемые ею T1 и T2 записываем в качестве левого и правого сына добавляемого элемента.


Операция remove

Операция remove(T,x) удаляет из дерева T элемент с ключом x.

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

  1. Разобьём наше дерево по ключу, который мы хотим удалить, то есть split(T,k.x){T1,T2}.
  2. Теперь отделяем от первого дерева элемент x, опять таки разбивая по ключу x, то есть split(T1,k.xε){T1,T3}.
  3. Сливаем первое дерево со вторым, то есть merge(T1,T2)T.

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

Спускаемся по дереву (как в обычном бинарном дереве поиска по x), ища удаляемый элемент. Найдя элемент, мы просто вызываем merge его левого и правого сыновей, и возвращаемое ею значение ставим на место удаляемого элемента, то есть merge(T.l,T.t)T.


Литература

Todo