Декартово дерево по неявному ключу — различия между версиями
(→Основная идея) |
(→Операции, поддерживающие структуру декартова дерева) |
||
Строка 12: | Строка 12: | ||
==Операции, поддерживающие структуру декартова дерева== | ==Операции, поддерживающие структуру декартова дерева== | ||
− | Структура обычного декартова дерева поддерживается с помощью двух операций: <tex>split</tex> {{---}} разбиение одного декартова дерева на два таких, что в одном ключ <tex>X</tex> меньше, чем заданное значение, а в другом {{---}} больше, и <tex>merge</tex> {{---}} слияние двух деревьев, в одном из которых все ключи <tex>X</tex> меньше, чем во втором. С учетом отличий декартова дерева по неявному ключу от обычного, операции теперь будут описываться так: <tex>split(root, t)</tex> {{---}} разбиение дерева на два так, что в левом окажется ровно <tex>t</tex> вершин, и <tex>merge(root1, | + | Структура обычного декартова дерева поддерживается с помощью двух операций: <tex>\mathrm{split}</tex> {{---}} разбиение одного декартова дерева на два таких, что в одном ключ <tex>X</tex> меньше, чем заданное значение, а в другом {{---}} больше, и <tex>\mathrm{merge}</tex> {{---}} слияние двух деревьев, в одном из которых все ключи <tex>X</tex> меньше, чем во втором. С учетом отличий декартова дерева по неявному ключу от обычного, операции теперь будут описываться так: <tex>\mathrm{split(root, t)}</tex> {{---}} разбиение дерева на два так, что в левом окажется ровно <tex>t</tex> вершин, и <tex>\mathrm{merge(root1, root)}</tex> {{---}} слияние двух любых деревьев, соответственно. |
===Split=== | ===Split=== | ||
− | Пусть процедура <tex> | + | Пусть процедура <tex>\mathrm{split}</tex> запущена в корне дерева с требованием отрезать от дерева <tex>t</tex> вершин. Также известно, что в левом поддереве вершины находится <tex>l</tex> вершин, а в правом <tex>r</tex>. Рассмотрим все возможные случаи: |
− | * <tex>l = t</tex>. В этом случае процедура <tex> | + | * <tex>l = t</tex>. В этом случае процедура <tex>\mathrm{split}</tex> должна просто пометить, что у корня больше нет левого сына, и вернуть его бывшего левого сына в качестве левой части ответа, а сам корень {{---}} в качестве правой. |
* Случай (<tex>t = l + 1</tex>) рассматривается аналогично предыдущему. | * Случай (<tex>t = l + 1</tex>) рассматривается аналогично предыдущему. | ||
− | * <tex>t < l</tex>. В этом случае нужно рекурсивно запустить процедуру <tex> | + | * <tex>t < l</tex>. В этом случае нужно рекурсивно запустить процедуру <tex>\mathrm{split}</tex> от левого сына с тем же параметром <tex>t</tex>, и левая часть сына станет левой частью ответа нашей процедуры, а правая часть сына станет левым сыном корня, после чего корень станет правой частью ответа. |
* Случай <tex>t > l + 1</tex> рассматривается аналогично предыдущему, с той лишь разницей, что от правого сына отрезается <tex>t - l - 1</tex> вершин. | * Случай <tex>t > l + 1</tex> рассматривается аналогично предыдущему, с той лишь разницей, что от правого сына отрезается <tex>t - l - 1</tex> вершин. | ||
===Merge=== | ===Merge=== | ||
− | Посмотрим любую из [[Декартово дерево#Операция merge|реализаций]] процедуры <tex> | + | Посмотрим любую из [[Декартово дерево#Операция merge|реализаций]] процедуры <tex>\mathrm{merge}</tex>. Заметим, что в ней программа ни разу не обращается к ключу <tex>X</tex>. Поэтому реализация процедуры <tex>\mathrm{merge}</tex> для декартова дерева по неявному ключу вообще не будет отличаться от реализации той же процедуры в обычном декартовом дереве. |
===Поддержание корректности значений C=== | ===Поддержание корректности значений C=== |
Версия 16:28, 25 мая 2015
Содержание
Основная идея
Возьмем структуру данных вектор. В её стандартной реализации мы умеем добавлять элемент в конец вектора, узнавать значение элемента, стоящего на определенной позиции, изменять элемент по номеру и удалять последний элемент. Предположим, что нам необходима структура данных с вышеуказанными свойствами, а также с операциями: добавить элемент в любое место (с соответствующим изменением нумерации элементов) и удалить любой элемент (также с соответствующим изменением нумерации). Такую структуру можно реализовать на базе декартового дерева, результат часто называют декартово дерево по неявному ключу.
Ключ X
Как известно, декартово дерево — это структура данных, объединяющая в себе бинарное дерево поиска и бинарную кучу. При реализации же декартова дерева по неявному ключу модифицируем эту структуру. А именно, оставим в нем только приоритет , а вместо ключа будем использовать следующую величину: количество элементов в нашей структуре, находящихся левее нашего элемента. Иначе говоря, будем считать ключом порядковый номер нашего элемента в дереве, уменьшенный на единицу.
Заметим, что при этом сохранится структура двоичного дерева поиска по этому ключу (то есть модифицированное декартово дерево так и останется декартовым деревом). Однако, с этим подходом появляется проблема: операции добавления и удаления элемента могут поменять нумерацию, и при наивной реализации на изменение всех ключей потребуется времени, где — количество элементов в дереве.
Вспомогательная величина С
Решается эта проблема довольно просто. Основная идея заключается в том, что такой ключ
сам по себе нигде не хранится. Вместо него будем хранить вспомогательную величину : количество вершин в поддереве нашей вершины (в поддерево включается и сама вершина). Обратим внимание, что все операции с обычным декартовым деревом делались сверху. Также заметим, что если по пути до некой вершины просуммировать все такие величины в левых поддеревьях, в которые мы не пошли, увеличенные на единицу, то придя в саму вершину и добавив к этой величине количество элементов в её левом поддереве, мы получим как раз ее ключ .Операции, поддерживающие структуру декартова дерева
Структура обычного декартова дерева поддерживается с помощью двух операций:
— разбиение одного декартова дерева на два таких, что в одном ключ меньше, чем заданное значение, а в другом — больше, и — слияние двух деревьев, в одном из которых все ключи меньше, чем во втором. С учетом отличий декартова дерева по неявному ключу от обычного, операции теперь будут описываться так: — разбиение дерева на два так, что в левом окажется ровно вершин, и — слияние двух любых деревьев, соответственно.Split
Пусть процедура
запущена в корне дерева с требованием отрезать от дерева вершин. Также известно, что в левом поддереве вершины находится вершин, а в правом . Рассмотрим все возможные случаи:- . В этом случае процедура должна просто пометить, что у корня больше нет левого сына, и вернуть его бывшего левого сына в качестве левой части ответа, а сам корень — в качестве правой.
- Случай ( ) рассматривается аналогично предыдущему.
- . В этом случае нужно рекурсивно запустить процедуру от левого сына с тем же параметром , и левая часть сына станет левой частью ответа нашей процедуры, а правая часть сына станет левым сыном корня, после чего корень станет правой частью ответа.
- Случай рассматривается аналогично предыдущему, с той лишь разницей, что от правого сына отрезается вершин.
Merge
Посмотрим любую из реализаций процедуры . Заметим, что в ней программа ни разу не обращается к ключу . Поэтому реализация процедуры для декартова дерева по неявному ключу вообще не будет отличаться от реализации той же процедуры в обычном декартовом дереве.
Поддержание корректности значений C
Единственное действие, обеспечивающее корректность этих значений заключается в том, что после любого действия с детьми вершины нужно записать в ее поле
сумму этих значений в ее новых детях, увеличенную на единицу.Применение описанного дерева
Таким образом, описана структура, от которой можно отрезать слева часть произвольной длины и слить две любые части в одну в нужном порядке. Теперь мы имеем возможность:
- вставить элемент в любое место (отрежем нужное количество элементов слева, сольем левое дерево с деревом из одного добавленного элемента и результат — с правым деревом);
- переставить любой кусок массива куда угодно (сделаем нужные разрезы и слияния в правильном порядке);
- совершать групповые операции с элементами. Вспомним реализацию таких операций в дереве отрезков и поймем, что ничего не помешает нам сделать то же самое с описанным деревом. В групповые операции включается, естественно, и взятие функции от отрезка.
- сделав на одном исходном массиве два дерева из элементов разной четности, можно решить задачу про смену мест четных и нечетных на отрезке.
- с помощью декартова дерева по неявному ключу можно эффективно реализовать такую структуру данных как Rope