Реализация запроса в дереве отрезков сверху — различия между версиями
Gr1n (обсуждение | вклад) (→Реализация) |
Gr1n (обсуждение | вклад) |
||
Строка 2: | Строка 2: | ||
==Алгоритм== | ==Алгоритм== | ||
− | + | Пусть есть уже [[Дерево отрезков. Построение|построенное дерево отрезков]] и идет запрос на отрезке <tex>[a .. b]</tex>. | |
− | + | [[Файл:123.jpg|right|380px|thumb|Пример дерева отрезков]] | |
− | |||
− | [[Файл:123.jpg|right|380px|thumb|Пример дерева отрезков | ||
Будем передавать в качестве параметров рекурсий следующие переменные: | Будем передавать в качестве параметров рекурсий следующие переменные: | ||
* <tex>node</tex> {{---}} номер(в массиве с деревом отрезков) текущей вершины дерева. | * <tex>node</tex> {{---}} номер(в массиве с деревом отрезков) текущей вершины дерева. | ||
− | |||
* <tex>a</tex>, <tex>b</tex> {{---}} левая и правая границы запрашиваемого отрезка. | * <tex>a</tex>, <tex>b</tex> {{---}} левая и правая границы запрашиваемого отрезка. | ||
+ | |||
+ | Пусть <tex>l</tex>, <tex>r</tex> {{---}} это левая и правая границы отрезков, за которые "отвечает" наша вершина, причем левые границы обоих отрезков - включительно, а правые - нет. В дальнейшем будем называть подобную структуру полуинтвервалом. | ||
Запустим рекурсивную процедуру от всего отрезка(то есть от корневой вершины). | Запустим рекурсивную процедуру от всего отрезка(то есть от корневой вершины). | ||
Строка 16: | Строка 15: | ||
Для текущего состояния проверяем следующие условия : | Для текущего состояния проверяем следующие условия : | ||
− | * Если текущий | + | * Если текущий полуинтервал не пересекается с искомым, то возвращаем нулевое значение. |
− | ''Например'': текущий <tex>[1.. | + | ''Например'': текущий <tex>[1..3)</tex>, а искомый <tex>[3 .. 5)</tex>; |
− | * Текущий | + | * Текущий полуинтервал совпадает, то возвращаем значение в текущей вершине. |
− | ''Например'': текущий и искомый <tex>[2.. | + | ''Например'': текущий и искомый <tex>[2..4)</tex>; |
− | * Иначе переходим к рекурсивным вызовам функций от детей вершины. При этом | + | * Иначе переходим к рекурсивным вызовам функций от детей вершины. При этом в зависимости от типа запроса возвращаем значение на текущем отрезке, как некоторую функцию от результатов выполнения на детях. |
''Замечание:'' | ''Замечание:'' | ||
− | При передаче новых параметров следует изменять не только границы, за которые отвечает текущая вершина, но и границы запрашиваемого | + | При передаче новых параметров следует изменять не только границы, за которые отвечает текущая вершина, но и границы запрашиваемого полуинтервала, чтобы на последующих шагах произошло полное совпадение полуинтервалов. |
− | Так как каждый | + | Так как каждый полуинтервал разбивается не более, чем на <tex>O(\log n)</tex> полуинтервал (поскольку на каждом уровне дерева может быть не более двух полуинтервалов из разбиения, а всего уровней <tex>\log n</tex> ), то данная реализация выполняется за <tex>O(\log n)</tex>. |
− | |||
− | + | ==Пример== | |
− | + | Рассмотрим данный алгоритм на примере задачи RSQ(запрос суммы на отрезке). | |
+ | |||
+ | Рассмотрим работу алгоритма для вычисления суммы на отрезке. Пусть дерево содержит 8 листьев и запрашиваемая сумма - это отрезок <tex>[1 .. 4]</tex>( полуинтервал <tex>[1 .. 5)</tex>) | ||
− | *Текущий | + | *Текущий полуинтервал <tex>[0 .. 8)</tex>, он больше <tex>[1 .. 5)</tex> => переходим по рекурсивным вызовам на <tex>[0 .. 4)</tex> и <tex>[4 .. 8)</tex> |
− | *<tex>[ | + | *<tex>[0 .. 4)</tex> выходит за границы<tex> [1 .. 5)</tex>, <tex>[4 .. 8)</tex> выходит за границы <tex>[1 .. 5)</tex> => переходим по рекурсивным вызовам на <tex>[0 .. 2)</tex>, <tex>[2 .. 4)</tex> и <tex>[4 .. 6)</tex>, <tex>[6 .. 8)</tex>. |
− | *<tex>[ | + | *<tex>[0 .. 2)</tex> выходит за границы <tex>[1 .. 5)</tex> => переходим в листья 0, 1; <tex>[2 .. 4)</tex> целиком внутри <tex>[1 .. 5)</tex> => возвращаем значение в <tex>[2 .. 4)</tex>; <tex>[6 .. 8)</tex> не пересекается с <tex>[1 .. 5)</tex> => возвращаем нулевое значение, <tex>[4 .. 6)</tex> выходит за границы <tex>[1 .. 5)</tex> => переходим к листьям <tex>4</tex> и <tex>5</tex> |
− | * | + | *листья <tex>5</tex> и <tex>0</tex> не пересекается с полуинтервалом <tex>[1 .. 5)</tex> => возвращаем нулевое значение, листья <tex>4</tex> и <tex>1</tex> целиков внутри <tex>[1 .. 5)</tex> => возвращаем значения в этих листьях. |
==Реализация== | ==Реализация== |
Версия 22:47, 28 апреля 2012
Данная рекурсивная операция позволяет выполнять запросы на дереве отрезков, причем алгоритм запускается от корня и рекурсивно идет сверху вниз.
Содержание
Алгоритм
Пусть есть уже построенное дерево отрезков и идет запрос на отрезке .
Будем передавать в качестве параметров рекурсий следующие переменные:
- — номер(в массиве с деревом отрезков) текущей вершины дерева.
- , — левая и правая границы запрашиваемого отрезка.
Пусть
, — это левая и правая границы отрезков, за которые "отвечает" наша вершина, причем левые границы обоих отрезков - включительно, а правые - нет. В дальнейшем будем называть подобную структуру полуинтвервалом.Запустим рекурсивную процедуру от всего отрезка(то есть от корневой вершины).
Для текущего состояния проверяем следующие условия :
- Если текущий полуинтервал не пересекается с искомым, то возвращаем нулевое значение.
Например: текущий
, а искомый ;- Текущий полуинтервал совпадает, то возвращаем значение в текущей вершине.
Например: текущий и искомый
;- Иначе переходим к рекурсивным вызовам функций от детей вершины. При этом в зависимости от типа запроса возвращаем значение на текущем отрезке, как некоторую функцию от результатов выполнения на детях.
Замечание:
При передаче новых параметров следует изменять не только границы, за которые отвечает текущая вершина, но и границы запрашиваемого полуинтервала, чтобы на последующих шагах произошло полное совпадение полуинтервалов.
Так как каждый полуинтервал разбивается не более, чем на
полуинтервал (поскольку на каждом уровне дерева может быть не более двух полуинтервалов из разбиения, а всего уровней ), то данная реализация выполняется за .
Пример
Рассмотрим данный алгоритм на примере задачи RSQ(запрос суммы на отрезке).
Рассмотрим работу алгоритма для вычисления суммы на отрезке. Пусть дерево содержит 8 листьев и запрашиваемая сумма - это отрезок
( полуинтервал )- Текущий полуинтервал , он больше => переходим по рекурсивным вызовам на и
- выходит за границы , выходит за границы => переходим по рекурсивным вызовам на , и , .
- выходит за границы => переходим в листья 0, 1; целиком внутри => возвращаем значение в ; не пересекается с => возвращаем нулевое значение, выходит за границы => переходим к листьям и
- листья и не пересекается с полуинтервалом => возвращаем нулевое значение, листья и целиков внутри => возвращаем значения в этих листьях.
Реализация
Рассмотрим реализацию рассматриваемой выше задачи RSQ.
int get_sum (int node, int a, int b) { // Рассматриваем в реализаций полуинтервалы l = tree[node].left; r = tree[node].right; if [l, r)[a, b) == return 0; if [l, r) == [a, b) return tree[node]; int m = (l + r) / 2; return get_sum (node * 2 + 1, a, min(b, m)) + get_sum (node * 2 + 2, max(a, m), b); }