Декартово дерево
Декартово дерево (также известное как декартово дерево поиска, декартово дерево с приоритетами, treap или дерево-куча) — это структура данных, сочетающая свойства двоичного дерева поиска (BST) и двоичной кучи (heap). Каждый узел декартова дерева хранит два ключа: основной ключ (x), по которому дерево является двоичным деревом поиска, и приоритет (y), по которому дерево является кучей (обычно максимум-кучей). Основное свойство: для любого узла все ключи в левом поддереве меньше его ключа, все ключи в правом поддереве больше, а приоритеты всех потомков меньше (или равны) приоритета самого узла.
История и происхождение
Декартово дерево было независимо описано несколькими исследователями в конце 1980-х — начале 1990-х годов. Название «treap» (tree + heap) ввёл Сесил Седжвик (Cecil Sedgewick) в 1989 году. В 1991 году Вуйцик (Wojciech Plandowski) и другие польские учёные предложили аналогичную структуру. В русскоязычной литературе термин «декартово дерево» закрепился благодаря работам Никлауса Вирта и последующим публикациям по алгоритмам.
Идея объединения свойств BST и кучи возникла из потребности в структуре данных, которая поддерживает быстрый поиск, вставку и удаление, но при этом остаётся сбалансированной с высокой вероятностью (без необходимости в явных операциях балансировки, как в красно-чёрных деревьях).
Основные свойства
Декартово дерево определяется двумя параметрами для каждого узла:
- Ключ (x) — значение, по которому строится BST. Обычно это уникальные значения, но могут быть и неуникальные (тогда требуется дополнительное правило, например, вставка одинаковых ключей в левое или правое поддерево).
- Приоритет (y) — случайное число, которое генерируется при создании узла. Приоритеты должны быть уникальными (или, по крайней мере, строго упорядочены), чтобы избежать неоднозначности.
Свойства:
- Свойство BST: для любого узла с ключом x все ключи в левом поддереве меньше x, а в правом — больше x.
- Свойство кучи: для любого узла с приоритетом y все приоритеты его потомков меньше y (если куча максимальная).
Из этих свойств следует, что корнем дерева всегда является узел с максимальным приоритетом (в случае максимум-кучи). Если приоритеты генерируются случайно, дерево получается сбалансированным с высокой вероятностью — средняя высота составляет O(log n), где n — количество узлов.
Устройство и операции
Узел декартова дерева
Каждый узел содержит:
- Ключ (x)
- Приоритет (y)
- Указатели на левого и правого потомка (left, right)
Основные операции
Вставка
Вставка нового узла с ключом x и случайным приоритетом y выполняется в два этапа:
- Сначала узел вставляется как в обычное BST: по ключу находится подходящее место (лист).
- Затем, если приоритет нового узла нарушает свойство кучи (больше приоритета родителя), выполняется поворот (вращение) — подъём узла вверх по дереву, аналогично операциям в куче.
Повороты бывают правые и левые:
- Правый поворот (right rotate): узел поднимается, заменяя своего родителя; левый потомок родителя становится правым потомком поднимаемого узла.
- Левый поворот (left rotate): симметрично.
Среднее время вставки — O(log n).
Удаление
Удаление узла по ключу также двухэтапное:
- Найти узел с заданным ключом (как в BST).
- Если узел найден, его необходимо «опустить» вниз до листа, выполняя повороты (меняя местами с потомком с большим приоритетом), пока он не станет листом. Затем узел удаляется.
Время удаления — O(log n) в среднем.
Поиск
Поиск узла по ключу выполняется как в обычном BST: от корня, сравнивая ключи, выбирая левое или правое поддерево. Время — O(log n) в среднем.
Слияние и разделение
Декартово дерево поддерживает две мощные операции:
- Split (разделение) — разделение дерева на два по ключу: все узлы с ключами меньше заданного остаются в левом дереве, остальные — в правом. Выполняется за O(log n).
- Merge (слияние) — объединение двух декартовых деревьев, где все ключи первого меньше всех ключей второго. Выполняется за O(log n).
Эти операции позволяют реализовать вставку и удаление без поворотов, а также поддерживать множество других структур (например, неявное декартово дерево).
Классификация и разновидности
По типу приоритетов
- Случайные приоритеты — классический вариант, где приоритеты генерируются случайно (например, с помощью датчика случайных чисел). Обеспечивает вероятностную сбалансированность.
- Детерминированные приоритеты — приоритеты вычисляются по ключу (например, хеш-функция). Может привести к вырождению, если функция неудачна.
Неявное декартово дерево (Implicit Treap)
В неявном декартовом дереве ключом является не значение, а позиция элемента в последовательности (индекс). Приоритеты — случайные. Это позволяет эффективно выполнять операции над массивами: вставка, удаление, разворот отрезка, сдвиг, копирование. Широко применяется в задачах на строки и последовательности.
Декартово дерево по сумме (Sum Treap)
В узлах дополнительно хранятся агрегированные значения (например, сумма поддерева). Позволяет быстро вычислять сумму на отрезке.
Применение
Декартово дерево нашло применение в различных областях информатики:
- Базы данных — для индексации данных (например, в PostgreSQL используется в некоторых реализациях индексов).
- Алгоритмы на графах — для построения минимальных остовных деревьев (алгоритм Крускала с использованием декартова дерева для слияния компонент).
- Генерация случайных перестановок — декартово дерево может быть использовано для равномерного распределения элементов.
- Обработка последовательностей — неявное декартово дерево применяется в редакторах текста, в задачах на строки (например, в алгоритме Бойера — Мура — Хорспула).
- Спортивное программирование — treap является одной из самых популярных структур данных на олимпиадах по информатике благодаря простоте реализации и универсальности.
Преимущества и недостатки
Преимущества
- Простота реализации (по сравнению с красно-чёрными или AVL-деревьями).
- Средняя высота O(log n) с высокой вероятностью.
- Поддержка операций слияния и разделения за логарифмическое время.
- Возможность реализации неявного дерева для работы с последовательностями.
Недостатки
- Вероятностная сбалансированность — в худшем случае (при неудачных случайных числах) высота может достигать O(n), хотя вероятность этого крайне мала.
- Требуется генерация случайных чисел, что может быть накладным в некоторых средах.
- Приоритеты занимают дополнительную память.
Интересные факты
- Название «treap» является контаминацией слов «tree» и «heap».
- Декартово дерево названо в честь Рене Декарта из-за аналогии с декартовой системой координат: каждый узел можно представить точкой на плоскости с координатами (x, y), где x — ключ, y — приоритет. Тогда дерево строится как геометрическая структура — «декартово дерево» в смысле «дерево, построенное на декартовых координатах».
- В 1996 году Роберт Тарьян (Robert Tarjan) и другие исследователи показали, что декартово дерево может быть построено за линейное время для отсортированных ключей.
- В некоторых реализациях (например, в библиотеке STL в C++) treap используется как основа для контейнеров
setиmap(в частности, в GCC).
Источники
- Седжвик, Роберт. «Алгоритмы на C++». — 5-е изд. — М.: Вильямс, 2016. — 1120 с.
- Кормен, Т. и др. «Алгоритмы: построение и анализ». — 3-е изд. — М.: Вильямс, 2013. — 1328 с.
- Вирт, Никлаус. «Алгоритмы и структуры данных». — М.: ДМК Пресс, 2010. — 272 с.
- Пландовски, В. и др. «Treap: A Randomised Binary Search Tree». — 1991.
- Тарьян, Р. «Efficient Algorithms for Sorting and Searching». — 1996.
- Документация GNU C++ Library (libstdc++), раздел «Tree Containers».
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →