Вам бонус- начислено 1 монета за дневную активность. Сейчас у вас 1 монета

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Лекция



Сразу хочу сказать, что здесь никакой воды про деревья поиска, и только нужная информация. Для того чтобы лучше понимать что такое деревья поиска, авл-дерево сплей-дерево, декартово дерево , настоятельно рекомендую прочитать все из категории Структуры данных.

« деревья поиска »

бывают следующих видов


2-3 дерево
B-дерево
Fusion tree
Rope
Splay-дерево
Tango-дерево
АВЛ-дерево
декартово дерево
Декартово дерево по неявному ключу
Дерево ван Эмде Боаса
Дерево поиска, наивная реализация
Красно-черное дерево
Обзор поисковых структур данных
Поисковые структуры данных
Рандомизированное бинарное дерево поиска
Сверхбыстрый цифровой бор
Упорядоченное множество

АВЛ-дерево

АВЛ-дерево — сбалансированное по высоте двоичное дерево поиска: для каждой его вершины высота ее двух поддеревьев различается не более чем на 1.

АВЛ — аббревиатура, образованная первыми буквами создателей (советских ученых) Адельсон-Вельского Георгия Максимовича и Ландиса Евгения Михайловича.

Везде утверждается, что АВЛ-деревья проще красно-черных деревьев, но глядя на прилагаемый к этому код, начинаешь сомневаться в данном утверждении. Собственно, желание объяснить на пальцах, как устроены АВЛ-деревья, и послужило мотивацией к написанию данного поста. Изложение иллюстрируется кодом на С++.

Понятие АВЛ-дерева


Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.АВЛ-дерево — это прежде всего двоичное дерево поиска, ключи которого удовлетворяют стандартному свойству: ключ любого узла дерева не меньше любого ключа в левом поддереве данного узла и не больше любого ключа в правом поддереве этого узла. Это значит, что для поиска нужного ключа в АВЛ-дереве можно использовать стандартный алгоритм. Для простоты дальнейшего изложения будем считать, что все ключи в дереве целочисленны и не повторяются.

Особенностью АВЛ-дерева является то, что оно является сбалансированным в следующем смысле:для любого узла дерева высота его правого поддерева отличается от высоты левого поддерева не более чем на единицу. Доказано, что этого свойства достаточно для того, чтобы высота дерева логарифмически зависела от числа его узлов: высота h АВЛ-дерева с n ключами лежит в диапазоне от log2(n + 1) до 1.44 log2(n + 2) − 0.328. А так как основные операции над двоичными деревьями поиска (поиск, вставка и удаление узлов) линейно зависят от его высоты, то получаем гарантированную логарифмическую зависимость времени работы этих алгоритмов от числа ключей, хранимых в дереве. Напомним, что рандомизированные деревья поиска обеспечивают сбалансированность только в вероятностном смысле: вероятность получения сильно несбалансированного дерева при больших n хотя и является пренебрежимо малой, но остается не равной нулю.

Структура узлов


Будем представлять узлы АВЛ-дерева следующей структурой:

struct node // структура для представления узлов дерева
{
	int key;
	unsigned char height;
	node* left;
	node* right;
	node(int k) { key = k; left = right = 0; height = 1; }
};


Поле key хранит ключ узла, поле height — высоту поддерева с корнем в данном узле, поля left и right — указатели на левое и правое поддеревья. Простой конструктор создает новый узел (высоты 1) с заданным ключом k.

Традиционно, узлы АВЛ-дерева хранят не высоту, а разницу высот правого и левого поддеревьев (так называемый balance factor), которая может принимать только три значения -1, 0 и 1. Однако, заметим, что эта разница все равно хранится в переменной, размер которой равен минимум одному байту (если не придумывать каких-то хитрых схем «эффективной» упаковки таких величин). Вспомним, что высота h < 1.44 log2(n + 2), это значит, например, что при n=109 (один миллиард ключей, больше 10 гигабайт памяти под хранение узлов) высота дерева не превысит величины h=44, которая с успехом помещается в тот же один байт памяти, что и balance factor. Таким образом, хранение высот с одной стороны не увеличивает объем памяти, отводимой под узлы дерева, а с другой стороны существенно упрощает реализацию некоторых операций.

Определим три вспомогательные функции, связанные с высотой. Первая является оберткой для поля height, она может работать и с нулевыми указателями (с пустыми деревьями):

unsigned char height(node* p)
{
	return p?p->height:0;
}


Вторая вычисляет balance factor заданного узла (и работает только с ненулевыми указателями):

int bfactor(node* p)
{
	return height(p->right)-height(p->left);
}


Третья функция восстанавливает корректное значение поля height заданного узла (при условии, что значения этого поля в правом и левом дочерних узлах являются корректными):

void fixheight(node* p)
{
	unsigned char hl = height(p->left);
	unsigned char hr = height(p->right);
	p->height = (hl>hr?hl:hr)+1;
}


Заметим, что все три функции являются нерекурсивными, т.е. время их работы есть величина О(1).

Максимальная высота

Максимальная высота АВЛ-дерева при заданном числе узлов :

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

где:

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. (округлить вверх),

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. (округлить вниз),

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. (округлить вниз).

Стоит отметить, что количество возможных высот на практике сильно ограничено (при 32-битной адресации максимальная высота равна 45, при 48-битной — 68), поэтому лучше, возможно, заранее подсчитать все значения минимального количества узлов для каждой высоты с помощью рекуррентной формулы для дерева Фибоначчи:

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.,

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.,

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Промежуточные значения количества узлов будут соответствовать предыдущей (меньшей) высоте.

Балансировка узлов


В процессе добавления или удаления узлов в АВЛ-дереве возможно возникновение ситуации, когда balance factor некоторых узлов оказывается равными 2 или -2, т.е. возникает расбалансировка поддерева. Для выправления ситуации применяются хорошо нам известные повороты вокруг тех или иных узлов дерева. Напомню, что простой поворот вправо (влево) производит следующую трансформацию дерева:

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Код, реализующий правый поворот, выглядит следующим образом (как обычно, каждая функция, изменяющая дерево, возвращает новый корень полученного дерева):

node* rotateright(node* p) // правый поворот вокруг p
{
	node* q = p->left;
	p->left = q->right;
	q->right = p;
	fixheight(p);
	fixheight(q);
	return q;
}


Левый поворот является симметричной копией правого:

node* rotateleft(node* q) // левый поворот вокруг q
{
	node* p = q->right;
	q->right = p->left;
	p->left = q;
	fixheight(q);
	fixheight(p);
	return p;
}


Рассмотрим теперь ситуацию дисбаланса, когда высота правого поддерева узла p на 2 больше высоты левого поддерева (обратный случай является симметричным и реализуется аналогично). Пусть q — правый дочерний узел узла p, а s — левый дочерний узел узла q.

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Анализ возможных случаев в рамках данной ситуации показывает, что для исправления расбалансировки в узле p достаточно выполнить либо простой поворот влево вокруг p, либо так называемый большой поворот влево вокруг того же p. Простой поворот выполняется при условии, что высота левого поддерева узла q больше высоты его правого поддерева: h(s)≤h(D).

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Большой поворот применяется при условии h(s)>h(D) и сводится в данном случае к двум простым — сначала правый поворот вокруг q и затем левый вокруг p.

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Код, выполняющий балансировку, сводится к проверке условий и выполнению поворотов:

node* balance(node* p) // балансировка узла p
{
	fixheight(p);
	if( bfactor(p)==2 )
	{
		if( bfactor(p->right) < 0 )
			p->right = rotateright(p->right);
		return rotateleft(p);
	}
	if( bfactor(p)==-2 )
	{
		if( bfactor(p->left) > 0  )
			p->left = rotateleft(p->left);
		return rotateright(p);
	}
	return p; // балансировка не нужна
}


Описанные функции поворотов и балансировки также не содержат ни циклов, ни рекурсии, а значит выполняются за постоянное время, не зависящее от размера АВЛ-дерева.

Балансировка

Относительно АВЛ-дерева балансировкой вершины называется операция, которая в случае разницы высот левого и правого поддеревьев = 2, изменяет связи предок-потомок в поддереве данной вершины так, что разница становится <= 1, иначе ничего не меняет. Указанный результат получается вращениями поддерева данной вершины.

Используются 4 типа вращений:

1.Малое левое вращение Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. Данное вращение используется тогда, когда высота b-поддерева — высота L = 2 и высота С <= высота R.

2.Большое левое вращение Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. Данное вращение используется тогда, когда высота b-поддерева — высота L = 2 и высота c-поддерева > высота R.

3.Малое правое вращение Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. Данное вращение используется тогда, когда высота b-поддерева — высота R = 2 и высота С <= высота L.

4.Большое правое вращение Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. Данное вращение используется тогда, когда высота b-поддерева — высота R = 2 и высота c-поддерева > высота L.

В каждом случае достаточно просто доказать то, что операция приводит к нужному результату и что полная высота уменьшается не более чем на 1 и не может увеличиться. Также можно заметить, что большое вращение это комбинация правого и левого малого вращения. Из-за условия балансированности высота дерева О(log(N)), где N- количество вершин, поэтому добавление элемента требует O(log(N)) операций.

Вставка ключей


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

node* insert(node* p, int k) // вставка ключа k в дерево с корнем p
{
	if( !p ) return new node(k);
	if( kkey )
		p->left = insert(p->left,k);
	else
		p->right = insert(p->right,k);
	return balance(p);
}


Чтобы проверить соответствие реализованного алгоритма вставки теоретическим оценкам для высоты АВЛ-деревьев, был проведен несложный вычислительный эксперимент. Генерировался массив из случайно расположенных чисел от 1 до 10000, далее эти числа последовательно вставлялись в изначально пустое АВЛ-дерево и измерялась высота дерева после каждой вставки. Полученные результаты были усреднены по 1000 расчетам. На следующем графике показана зависимость от n средней высоты (красная линия); минимальной высоты (зеленая линия); максимальной высоты (синяя линия). Кроме того, показаны верхняя и нижняя теоретические оценки.

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

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

Алгоритм добавления вершины

Показатель сбалансированности в дальнейшем будем интерпретировать как разность между высотой левого и правого поддерева, а алгоритм будет основаться на типе TAVLTree, описанном выше. Непосредственно при вставке (листу) присваивается нулевой баланс. Процесс включения вершины состоит из трех частей (данный процесс описан Никлаусом Виртом в «Алгоритмы и структуры данных»):

  1. Прохода по пути поиска, пока не убедимся, что ключа в дереве нет.
  2. Включения новой вершины в дерево и определения результирующих показателей балансировки.
  3. «Отступления» назад по пути поиска и проверки в каждой вершине показателя сбалансированности. Если необходимо — балансировка.

Будем возвращать в качестве результата функции, уменьшилась высота дерева или нет. Предположим, что процесс из левой ветви возвращается к родителю (рекурсия идет назад), тогда возможны три случая: { hl — высота левого поддерева, hr — высота правого поддерева } Включение вершины в левое поддерево приведет к

  1. hl < hr: выравняется hl = hr. Ничего делать не нужно.
  2. hl = hr: теперь левое поддерево будет больше на единицу, но балансировка пока не требуется.
  3. hl > hr: теперь hl — hr = 2, — требуется балансировка.

В третьей ситуации требуется определить балансировку левого поддерева. Если левое поддерево этой вершины (Tree^.left^.left) выше правого (Tree^.left^.right), то требуется большое правое вращение, иначе хватит малого правого. Аналогичные (симметричные) рассуждения можно привести и для включение в правое поддерево.

Удаление ключей


С удалением узлов из АВЛ-дерева, к сожалению, все не так шоколадно, как с рандомизированными деревьями поиска. Способа, основанного на слиянии (join) двух деревьев, ни найти, ни придумать не удалось. Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.Поэтому за основу был взят вариант, описываемый практически везде (и который обычно применяется и при удалении узлов из стандартного двоичного дерева поиска). Идея следующая: находим узел p с заданным ключом k (если не находим, то делать ничего не надо), в правом поддереве находим узел min с наименьшим ключом и заменяем удаляемый узел p на найденный узел min.

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

Пусть теперь правое поддерево у p есть. Находим минимальный ключ в этом поддереве. По свойству двоичного дерева поиска этот ключ находится в конце левой ветки, начиная от корня дерева. Применяем рекурсивную функцию:

node* findmin(node* p) // поиск узла с минимальным ключом в дереве p 
{
	return p->left?findmin(p->left):p;
}


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

node* removemin(node* p) // удаление узла с минимальным ключом из дерева p
{
	if( p->left==0 )
		return p->right;
	p->left = removemin(p->left);
	return balance(p);
}


Теперь все готово для реализации удаления ключа из АВЛ-дерева. Сначала находим нужный узел, выполняя те же действия, что и при вставке ключа:

node* remove(node* p, int k) // удаление ключа k из дерева p
{
	if( !p ) return 0;
	if( k < p->key )
		p->left = remove(p->left,k);
	else if( k > p->key )
		p->right = remove(p->right,k);	


Как только ключ k найден, переходим к плану Б: запоминаем корни q и r левого и правого поддеревьев узла p; удаляем узел p; если правое поддерево пустое, то возвращаем указатель на левое поддерево; если правое поддерево не пустое, то находим там минимальный элемент min, потом его извлекаем оттуда, слева к min подвешиваем q, справа — то, что получилось из r, возвращаем min после его балансировки.

	else //  k == p->key 
	{
		node* q = p->left;
		node* r = p->right;
		delete p;
		if( !r ) return q;
		node* min = findmin(r);
		min->right = removemin(r);
		min->left = q;
		return balance(min);
	}


При выходе из рекурсии не забываем выполнить балансировку:

	return balance(p);
}


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

Очевидно, что операции вставки и удаления (а также более простая операция поиска) выполняются за время пропорциональное высоте дерева, т.к. в процессе выполнения этих операций производится спуск из корня к заданному узлу, и на каждом уровне выполняется некоторое фиксированное число действий. А в силу того, что АВЛ-дерево является сбалансированным, его высота зависит логарифмически от числа узлов. Таким образом, время выполнения всех трех базовых операций гарантированно логарифмически зависит от числа узлов дерева.

Алгоритм удаления вершины

Для простоты опишем рекурсивный алгоритм удаления. Если вершина — лист, то удалим ее и вызовем балансировку всех ее предков в порядке от родителя к корню. Иначе найдем самую близкую по значению вершину в поддереве наибольшей высоты (правом или левом) и переместим ее на место удаляемой вершины, при этом вызвав процедуру ее удаления.

Докажем, что данный алгоритм сохраняет балансировку. Для этого докажем по индукции по высоте дерева, что после удаления некоторой вершины из дерева и последующей балансировки высота дерева уменьшается не более, чем на 1. База индукции: Для листа очевидно верно. Шаг индукции: Либо условие балансированности в корне (после удаления корень может изменится) не нарушилось, тогда высота данного дерева не изменилась, либо уменьшилось строго меньшее из поддеревьев => высота до балансировки не изменилась => после уменьшится не более чем на 1.

Очевидно, что в результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по второму вызову, нет одного из поддеревьев. Но поиск ближайшего каждый раз требует O(N) операций. Становится очевидной возможность оптимизации: поиск ближайшей вершины может быть выполнен по краю поддерева, что сокращает сложность до O(log(N)).

Нерекурсивная вставка в АВЛ-дерево сверху-вниз

Нерекурсивный алгоритм сложнее рекурсивного.

  1. Находится место вставки и вершина, высота которой не изменится при вставке (это вершина, у которой высота левого поддерева не равна высоте правого; будем называть ее PrimeNode)
  2. Выполняется спуск от PrimeNode до места вставки с изменением балансов
  3. Выполняется ребалансировка PrimeNode при наличии переполнения

Нерекурсивное удаление из АВЛ-дерева сверху вниз

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

  1. высота левого поддерева равна высоте правого поддерева (исключая случай, когда у листа нет поддеревьев)
  2. высота дерева по направлению движения меньше противоположной(«брат» направления) и баланс «брата» равен 0 (разбор этого варианта довольно сложен, так что пока без доказательства)

Для облегчения понимания приведенный алгоритм не содержит каких-либо оптимизаций. В отличие от рекурсивного алгоритма, найденная удаляемая вершина заменяется значением из левой подветви. Этот алгоритм можно оптимизировать так же, как и рекурсивный (за счет того, что после нахождения удаляемой вершины известно направление движения):

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

Расстановка балансов при удалении

Как уже говорилось, если удаляемая вершина — лист, то она удаляется, и обратный обход дерева происходит от родителя удаленного листа. Если не лист — ей находится «замена», и обратный обход дерева происходит от родителя «замены». Непосредственно после удаления элемента — «замена» получает баланс удаляемого узла.

При обратном обходе: если при переходе к родителю пришли слева — баланс увеличивается на 1, если же пришли справа — уменьшается на 1.

Это делается до тех пор, пока при изменении баланса он не станет равным −1 или 1 (обратите внимание на различие с вставкой элемента!): в данном случае такое изменение баланса будет гласить о неизменной дельта-высоте поддеревьев. Повороты происходят по тем же правилам, что и при вставке.

Расстановка балансов при одинарном повороте

Обозначим:

  • «Current» — узел, баланс которого равен −2 или 2: то есть тот, который нужно повернуть (на схеме — элемент a)
  • «Pivot» — ось вращения. +2: левый сын Current’а, −2: правый сын Current’а (на схеме — элемент b)

Если поворот осуществляется при вставке элемента, то баланс Pivot’а равен либо 1, либо −1. В таком случае после поворота балансы обоих устанавливаются равными 0. При удалении все иначе: баланс Pivot’а может стать равным 0 (в этом легко убедиться).

Приведем сводную таблицу зависимости финальных балансов от направления поворота и исходного баланса узла Pivot:

Направление поворота Old Pivot.Balance New Current.Balance New Pivot.Balance
Левый или Правый -1 или +1 0 0
Правый 0 -1 +1
Левый 0 +1 -1

Расстановка балансов при двойном повороте

Pivot и Current — те же самые, но добавляется третий участник поворота. Обозначим его за «Bottom»: это (при двойном правом повороте) левый сын Pivot’а, а при двойном левом — правый сын Pivot’а.

При данном повороте — Bottom в результате всегда приобретает баланс 0, но от его исходного баланса зависит расстановка балансов для Pivot и Current.

Приведем сводную таблицу зависимости финальных балансов от направления поворота и исходного баланса узла Bottom:

Направление Old Bottom.Balance New Current.Balance New Pivot.Balance
Левый или Правый 0 0 0
Правый +1 0 -1
Правый -1 +1 0
Левый +1 -1 0
Левый -1 0 +1

Оценка эффективности

Из формулы, приведенной выше, высота АВЛ-дерева никогда не превысит высоту идеально сбалансированного дерева более, чем на 45%. Для больших Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. имеет место оценка Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Таким образом, выполнение основных операций требует порядка Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. сравнений. Экспериментально выяснено, что одна балансировка приходится на каждые 2 включения и на каждые 5 исключений.

Расширяющееся дерево (splay tree) или косое дерево)

Расширяющееся (англ. splay tree) или косое дерево является двоичным деревом поиска, в котором поддерживается свойство сбалансированности. Это дерево принадлежит классу «саморегулирующихся деревьев», которые поддерживают необходимый баланс ветвления дерева, чтобы обеспечить выполнение операций поиска, добавления и удаления за логарифмическое время от числа хранимых элементов. Об этом говорит сайт https://intellect.icu . Это реализуется без использования каких-либо дополнительных полей в узлах дерева (как, например, в Красно-черных деревьях или АВЛ-деревьях, где в вершинах хранится, соответственно, цвет вершины и глубина поддерева). Вместо этого «расширяющие операции» (splay operation), частью которых являются вращения, выполняются при каждом обращении к дереву. Учетная стоимость (англ.) в расчете на одну операцию с деревом составляет Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Расширяющееся дерево придумали Роберт Тарьян и Даниель Слейтор в 1983 г.

Сплей-дерево (англ. Splay-tree) — это двоичное дерево поиска. Оно позволяет находить быстрее те данные, которые использовались недавно. Относится к разряду сливаемых деревьев. Сплей-дерево было придумано Робертом Тарьяном и Даниелем Слейтером в 1983 году.

Эвристики

Для того, чтобы доступ к недавно найденным данным был быстрее, надо, чтобы эти данные находились ближе к корню. Этого мы можем добиться, используя различные эвристики:

  • Move to Root — совершает повороты вокруг ребра Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., где Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — найденная вершина, Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — ее предок, пока Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. не окажется корнем дерева. Однако можно построить такую последовательность операций, что амортизированное время доступа к вершине будет Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
  • Splay — также совершает повороты, но чередует различные виды поворотов, благодаря чему достигается логарифмическая амортизированная оценка. Она будет подробно описана ниже.

Пример: При последовательном использовании операций "move to root" для вершин Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. требуется по 6 поворотов, в то время как при использовании операции "splay" для вершины Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.достаточно 3 поворотов.

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Операции со splay-деревом

splay(tree, x)

"splay" делится на 3 случая:

zig

Если Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — корень дерева с сыном Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., то совершаем один поворот вокруг ребра Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., делая Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. корнем дерева. Данный случай является крайним и выполняется только один раз в конце, если изначальная глубина Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. была нечетной.

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

zig-zig

Если Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — не корень дерева, а Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — оба левые или оба правые дети, то делаем поворот ребра Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., где Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. отец Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., а затем поворот ребра Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

zig-zag

Если Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — не корень дерева и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — левый ребенок, а Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — правый, или наоборот, то делаем поворот вокруг ребра Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., а затем поворот нового ребра Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., где Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — бывший родитель Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Данная операция занимает Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. времени, где Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — длина пути от Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. до корня.

find(tree, x)

Эта операция выполняется как для обычного бинарного дерева, только после нее запускается операция splay.

merge(tree1, tree2)

У нас есть два дерева Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., причем подразумевается, что все элементы первого дерева меньше элементов второго. Запускаем splay от самого большого элемента в дереве Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. (пусть это элемент Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.). После этого корень Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. содержит элемент Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., при этом у него нет правого ребенка. Делаем Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. правым поддеревом Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и возвращаем полученное дерево.

split(tree, x)

Запускаем splay от элемента Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и возвращаем два дерева, полученные отсечением правого или левого поддерева от корня, в зависимости от того, содержит корень элемент больше или не больше, чем Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

add(tree, x)

Запускаем split(tree, x), который нам возвращает деревья Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., их подвешиваем к Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. как левое и правое поддеревья соответственно.

remove(tree, x)

Запускаем splay от Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. элемента и возвращаем Merge от его детей.

Анализ операции splay

Амортизационный анализ сплей-дерева проводится с помощью метода потенциалов. Потенциалом рассматриваемого дерева назовем сумму рангов его вершин. Ранг вершины Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — это величина, обозначаемая Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и равная Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., где Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — количество вершин в поддереве с корнем в Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Лемма:
Амортизированное время операции splay вершины Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. в дереве с корнем Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. не превосходит Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.
Доказательство:
Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Проанализируем каждый шаг операции splay. Пусть Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — ранги вершин после шага и до него соответственно, Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — предок вершины Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., а Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — предок Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. (если есть).

Разберем случаи в зависимости от типа шага:

zig. Поскольку выполнен один поворот, то амортизированное время выполнения шага Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. (поскольку только у вершин Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. меняется ранг). Ранг вершины Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. уменьшился, поэтому Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Ранг вершины Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. увеличился, поэтому Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Следовательно, Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

zig-zig. Выполнено два поворота, амортизированное время выполнения шага Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Поскольку после поворотов поддерево с корнем в Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. будет содержать все вершины, которые были в поддереве с корнем в Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. (и только их), поэтому Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Используя это равенство, получаем: Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., поскольку Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Далее, так как Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., получаем, что Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Мы утверждаем, что эта сумма не превосходит Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., то есть, что Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Преобразуем полученное выражение следующим образом: Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Из рисунка видно, что Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., значит, сумма выражений под логарифмами не превосходит единицы. Далее, рассмотрим сумму логарифмов Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. При Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. произведение Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. по неравенству между средними не превышает Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. А поскольку логарифм — функция возрастающая, то Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., что и является требуемым неравенством.

zig-zag. Выполнено два поворота, амортизированное время выполнения шага Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Поскольку Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., то Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Далее, так как Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., то Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Мы утверждаем, что эта сумма не превосходит Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., то есть, что Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Но, поскольку Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. - аналогично доказанному ранее, что и требовалось доказать.

Итого, получаем, что амортизированное время шага zig-zag не превосходит Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

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

Статическая оптимальность сплей-дерева

Теорема:

Если к ключам Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., сложенным в сплей-дерево выполняется Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. запросов, к Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.-му ключу осуществляется Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. запросов, где Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., то суммарное время работы не превышает Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., где Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — шенноновская энтропия

Доказательство:
Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Известно, что Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — шенноновская энтропия.

Пусть Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — количество вершин в поддереве с корнем в Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. А Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — ранг вершины.

Обозначим за Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. корень Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.-дерева. Из предыдущей теоремы известно, что Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Пусть Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., тогда Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Так как вершина Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — корень Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.-дерева, то очевидно, что Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., следовательно Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Поэтому Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., ч.т.д.
Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Теорема о близких запросах в сплей-дереве

Теорема (о близких запросах в сплей-дереве):

Пусть в сплей-дерево сложены ключи Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Зафиксируем один из ключей Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Пусть выполняется Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. запросов к ключам. Тогда суммарное время на запросы есть Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., где Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — значение в вершине, к которой обращаются в Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.-ый запрос.

Доказательство:
Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Для доказательства теоремы воспользуемся методом потенциалов:

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

По условию выполняется Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. запросов, следовательно

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Введем следующие обозначения:

  • Весом узла с ключом Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. будем называть величину Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
  • Размером узла, содержащего ключ Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., будем называть величину Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., где Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — узлы поддерева с корнем в Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
  • Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — ранг узла.
  • Потенциал дерева после Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.-го запроса обозначим как Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Пусть Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — вес дерева. Тогда Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Последнее верно, так как при фиксированном Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., начиная с некоторого места, а именно Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., ряд сходится.

Из определения размера узла следует, что Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Также заметим, что для любого Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. от Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. до Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. верно, что Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., так как максимальное значение знаменателя в определении Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. достигается при Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. или наоборот.

Тогда, воспользовавшись полученными оценками, найдем изменение потенциала сплей-дерева после Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. запросов:

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Первое неравенство верно, так как максимальное значение потенциала достигается при Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., а минимальное при Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., а значит изменение потенциала не превышает разности этих величин.

Обозначим за Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. корень сплей-дерева. Тогда, воспользовавшись вышеуказанной леммой (можно показать, что она верна для любого фиксированного определения веса узла) получаем, что

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Докажем, что данное определение потенциала удовлетворяет условию теоремы о методе потенциалов.

Для любого Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. верно, что Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., так как Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., как было показано выше. Так как количество операций на запрос Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., то Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., где Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — функция из теоремы о методе потенциалов, равная в данном случае Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Следовательно, потенциал удовлетворяет условию теоремы.

Тогда, подставляя найденные значения в формулу Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., получаем, что

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Данная теорема показывает, что сплей-деревья поддерживают достаточно эффективный доступ к ключам, которые находятся близко к какому-то фиксированному ключу.

Splay-деревья по неявному ключу

Splay-дерево по неявному ключу полностью аналогично декартову дереву по неявному ключу, неявным ключом также будет количество элементов дерева, меньших данного. Аналогично, будем хранить вспомогательную величину Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — количество вершин в поддереве. К операциям, которые уже были представлены в декартовом дереве, добавляется splay, но пересчет Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. в ней тривиален, так как мы точно знаем, куда перемещаются изменяемые поддеревья.

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

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

Декартово дерево — это двоичное дерево, в узлах которого хранятся:

  • ссылки на правое и левое поддерево;
  • ссылка на родительский узел (необязательно);
  • ключи x и y, которые являются двоичным деревом поиска по ключу x и двоичной кучей по ключу y; а именно, для любого узла дерева n:
    • ключи x узлов правого (левого) поддерева больше (меньше) либо равны ключа x узла n;
    • ключи y узлов правого и левого детей больше либо равны ключу y узла n.

Ссылка на родительский узел не обязательна, она желательна только для линейного алгоритма построения дерева.

Декартово дерево не является самобалансирующимся в обычном смысле, и применяют его по следующим причинам:

  • Проще реализуется, по сравнению, например, с настоящими самобалансирующимися деревьями вроде красно-черного.
  • Хорошо ведет себя «в среднем», если ключи y раздать случайно.
  • Типичная для сортирующего дерева операция «расчленить по ключу x на „меньше x0“ и „не меньше x0“» работает за O(h), где h — высота дерева. На красно-черных деревьях придется восстанавливать балансировку и окраску узлов.

Недостатки декартового дерева:

  • Большие накладные расходы на хранение: вместе с каждым элементом хранятся два-три указателя и случайный ключ y.
  • Скорость доступа O(n) в худшем, хотя и маловероятном, случае. Поэтому декартово дерево недопустимо, например, в ядрах ОС.

Более строго, это бинарное дерево, в узлах которого хранится пары Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., где Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — это ключ, а Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — это приоритет. Также оно является двоичным деревом поиска по Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и пирамидой по Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Предполагая, что все Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и все Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. являются различными, получаем, что если некоторый элемент дерева содержит Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., то у всех элементов в левом поддереве Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., у всех элементов в правом поддереве Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., а также и в левом, и в правом поддереве имеем: Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

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

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

split

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Операция split

Операция Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. (разрезать) позволяет сделать следующее, разрезать исходное дерево Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. по ключу Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Возвращать она будет такую пару деревьев Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., что в дереве Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. ключи меньше Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., а в дереве Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. все остальные:Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

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

Рассмотрим случай, в котором требуется разрезать дерево по ключу, большему ключа корня. Посмотрим, как будут устроены результирующие деревья Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.:

  • Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.: левое поддерево Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. совпадет с левым поддеревом Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Для нахождения правого поддерева Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., нужно разрезать правое поддерево Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. на Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. по ключу Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и взять Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
  • Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. совпадет с Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

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

Псевдокод

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.Treap, TreapДеревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. split(t: Treap, k: int):
  if t == Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.
    return Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.,Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.
     
  else if k > t.x 
    

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.t1, t2Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. = split(t.right, k)

    t.right = t1
    return Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.t, t2Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.
  else 
    

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.t1, t2Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. = split(t.left, k)

    t.left = t2
    return Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.t1, tДеревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Время работы

Оценим время работы операции Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Во время выполнения вызывается одна операция Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. для дерева хотя бы на один меньшей высоты и делается еще Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. операций. Тогда итоговая трудоемкость этой операции равна Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., где Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — высота дерева.

merge

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Операция merge

Рассмотрим вторую операцию с декартовыми деревьями — Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. (слить).

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

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

Если Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. корня Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. больше Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. корня Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., то он и будет являться корнем. Тогда левое поддерево Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. совпадет с левым поддеревом Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Справа же нужно подвесить объединение правого поддерева Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и дерева Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Псевдокод

Treap merge(t1: Treap, t2: Treap):
  if t2 == Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.
    return t1
  if t1 ==Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.
    return t2
  else if t1.y > t2.y
    t1.right = merge(t1.right, t2)
    return t1
  else 
    t2.left = merge(t1, t2.left)
    return t2

Время работы

Рассуждая аналогично операции Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. приходим к выводу, что трудоемкость операции Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. равна Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., где Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — высота дерева.

insert

Операция Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. добавляет в дерево Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. элемент Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., где Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — ключ, а Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.— приоритет.

Представим что элемент Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., это декартово дерево из одного элемента, и для того чтобы его добавить в наше декартово дерево Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., очевидно, нам нужно их слить. Но Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. может содержать ключи как меньше, так и больше ключа Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., поэтому сначала нужно разрезать Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. по ключу Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

  • Реализация №1
  1. Разобьем наше дерево по ключу, который мы хотим добавить, то есть Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
  2. Сливаем первое дерево с новым элементом, то есть Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
  3. Сливаем получившиеся дерево со вторым, то есть Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
  • Реализация №2
  1. Сначала спускаемся по дереву (как в обычном бинарном дереве поиска по Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.), но останавливаемся на первом элементе, в котором значение приоритета оказалось меньше Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
  2. Теперь вызываем Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. от найденного элемента (от элемента вместе со всем его поддеревом)
  3. Полученные Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. записываем в качестве левого и правого сына добавляемого элемента.
  4. Полученное дерево ставим на место элемента, найденного в первом пункте.

В первой реализации два раза используется Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., а во второй реализации слияние вообще не используется.

remove

Операция Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. удаляет из дерева Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. элемент с ключом Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

  • Реализация №1
  1. Разобьем наше дерево по ключу, который мы хотим удалить, то есть Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
  2. Теперь отделяем от первого дерева элемент Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., то есть самого левого ребенка дерева Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
  3. Сливаем первое дерево со вторым, то есть Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
  • Реализация №2
  1. Спускаемся по дереву (как в обычном бинарном дереве поиска по Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.), и ищем удаляемый элемент.
  2. Найдя элемент, вызываем Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. его левого и правого сыновей
  3. Результат процедуры Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. ставим на место удаляемого элемента.

В первой реализации один раз используется Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., а во второй реализации разрезание вообще не используется.

Построение декартова дерева

Пусть нам известно из каких пар Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. требуется построить декартово дерево, причем также известно, что Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Алгоритм за Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

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

Другой алгоритм за Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Отсортируем парочки Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. по убыванию Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и положим их в очередь. Сперва достанем из очереди первый Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. элемента и сольем их в дерево и положим в конец очереди, затем сделаем то же самое со следующими двумя и т.д. Таким образом, мы сольем сначала Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. деревьев размера Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., затем Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. деревьев размера Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и так далее. При этом на уменьшение размера очереди в два раза мы будем тратить суммарно Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. время на слияния, а всего таких уменьшений будет Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Значит полное время работы алгоритма будет Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Алгоритм за Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Будем строить дерево слева направо, то есть начиная с Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. по Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., при этом помнить последний добавленный элемент Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Он будет самым правым, так как у него будет максимальный ключ, а по ключам декартово дерево представляет собой двоичное дерево поиска. При добавлении Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., пытаемся сделать его правым сыном Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., это следует сделать если Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., иначе делаем шаг к предку последнего элемента и смотрим его значение Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Поднимаемся до тех пор, пока приоритет в рассматриваемом элементе меньше приоритета в добавляемом, после чего делаем Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. его правым сыном, а предыдущего правого сына делаем левым сыном Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..


Заметим, что каждую вершину мы посетим максимум дважды: при непосредственном добавлении и, поднимаясь вверх (ведь после этого вершина будет лежать в чьем-то левом поддереве, а мы поднимаемся только по правому). Из этого следует, что построение происходит за Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Случайные приоритеты

Мы уже выяснили, что сложность операций с декартовым деревом линейно зависит от его высоты. В действительности высота декартова дерева может быть линейной относительно его размеров. Например, высота декартова дерева, построенного по набору ключей Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., будет равна Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Во избежание таких случаев, полезным оказывается выбирать приоритеты в ключах случайно.

Высота в декартовом дереве с случайными приоритетами

Теорема:

В декартовом дереве из Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. узлов, приоритеты Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. которого являются случайными величинами c равномерным распределением, средняя глубина вершины Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Доказательство:
Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Будем считать, что все выбранные приоритеты Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. попарно различны.

Для начала введем несколько обозначений:

  • Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — вершина с Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.-ым по величине ключом;
  • индикаторная величина Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.
  • Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — глубина вершины Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.;

В силу обозначений глубину вершины можно записать как количество предков:

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Теперь можно выразить математическое ожидание глубины конкретной вершины:

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — здесь мы использовали линейность математического ожидания, и то что Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. для индикаторной величины Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. (Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — вероятность события Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.).

Для подсчета средней глубины вершин нам нужно сосчитать вероятность того, что вершина Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. является предком вершины Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., то есть Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Введем новое обозначение:

  • Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — множество ключей Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. или Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., в зависимости от Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. или Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. обозначают одно и тоже, их мощность равна Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
Лемма:
Для любых Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. , Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. является предком Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. тогда и только тогда, когда Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. имеет наибольший приоритет среди Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
Доказательство:
Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Если Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. является корнем, то оно является предком Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и по определению имеет максимальный приоритет среди всех вершин, следовательно, и среди Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

С другой стороны, если Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — корень, то Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — не предок Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. имеет максимальный приоритет в декартовом дереве; следовательно, Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. не имеет наибольший приоритет среди Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Теперь предположим, что какая-то другая вершина Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — корень. Тогда, если Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. лежат в разных поддеревьях, то Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. или Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., следовательно, Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. содержится в Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.. В этом случае Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. — не предок Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево., и наибольший приоритет среди Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. имеет вершина с номером Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Наконец, если Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. лежат в одном поддереве, то доказательство применяется по индукции: пустое декартово дерево есть тривиальная база, а рассматриваемое поддерево является меньшим декартовым деревом.
Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Так как распределение приоритетов равномерное, каждая вершина среди Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. может иметь максимальный приоритет, мы немедленно приходим к следующему равенству:

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Подставив последнее в нашу формулу с математическим ожиданием получим:

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. (здесь мы использовали неравенство Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.)

Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. отличается от Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. в константу раз, поэтому Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

В итоге мы получили что Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..
Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево.

Таким образом, среднее время работы операций Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. и Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево. будет Деревья поиска, АВЛ-дерево Сплей-дерево. Декартово дерево..

Вау!! 😲 Ты еще не читал? Это зря!

декартово дерево по неявному ключу ,

задачи rmq , задачи lca ,

А как ты думаешь, при улучшении деревья поиска, будет лучше нам? Надеюсь, что теперь ты понял что такое деревья поиска, авл-дерево сплей-дерево, декартово дерево и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Структуры данных

создано: 2014-08-18
обновлено: 2021-03-13
134002



Рейтиг 1 of 10. count vote: 2
Вы довольны ?:


Поделиться:

Найди готовое или заработай

С нашими удобными сервисами без комиссии*

Как это работает? | Узнать цену?

Найти исполнителя
$0 / весь год.
  • У вас есть задание, но нет времени его делать
  • Вы хотите найти профессионала для выплнения задания
  • Возможно примерение функции гаранта на сделку
  • Приорететная поддержка
  • идеально подходит для студентов, у которых нет времени для решения заданий
Готовое решение
$0 / весь год.
  • Вы можите продать(исполнителем) или купить(заказчиком) готовое решение
  • Вам предоставят готовое решение
  • Будет предоставлено в минимальные сроки т.к. задание уже готовое
  • Вы получите базовую гарантию 8 дней
  • Вы можете заработать на материалах
  • подходит как для студентов так и для преподавателей
Я исполнитель
$0 / весь год.
  • Вы профессионал своего дела
  • У вас есть опыт и желание зарабатывать
  • Вы хотите помочь в решении задач или написании работ
  • Возможно примерение функции гаранта на сделку
  • подходит для опытных студентов так и для преподавателей



Комментарии


Оставить комментарий
Если у вас есть какое-либо предложение, идея, благодарность или комментарий, не стесняйтесь писать. Мы очень ценим отзывы и рады услышать ваше мнение.
To reply

Структуры данных

Термины: Структуры данных