Реализация запроса в дереве отрезков сверху — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Реализация)
Строка 2: Строка 2:
  
 
==Алгоритм==
 
==Алгоритм==
 
+
Пусть есть уже [[Дерево отрезков. Построение|построенное дерево отрезков]] и идет запрос на отрезке <tex>[a .. b]</tex>.
==Пример==
+
[[Файл:123.jpg|right|380px|thumb|Пример дерева отрезков]]
Рассмотрим данный алгоритм на примере задачи RSQ(запрос суммы на отрезке), то есть пусть есть уже [[Дерево отрезков. Построение|построенное дерево отрезков]] построенное дерево отрезков и задача найти сумму на отрезке <tex>[a .. b]</tex>.
 
[[Файл:123.jpg|right|380px|thumb|Пример дерева отрезков для вычисления сумм]]
 
  
 
Будем передавать в качестве параметров рекурсий следующие переменные:
 
Будем передавать в качестве параметров рекурсий следующие переменные:
 
* <tex>node</tex> {{---}} номер(в массиве с деревом отрезков) текущей вершины дерева.
 
* <tex>node</tex> {{---}} номер(в массиве с деревом отрезков) текущей вершины дерева.
* <tex>l</tex>, <tex>r</tex> {{---}} левая и правая границы отрезков, за которые "отвечает" наша вершина.
 
 
* <tex>a</tex>, <tex>b</tex>  {{---}} левая и правая границы запрашиваемого отрезка.
 
* <tex>a</tex>, <tex>b</tex>  {{---}} левая и правая границы запрашиваемого отрезка.
 +
 +
Пусть <tex>l</tex>, <tex>r</tex> {{---}} это левая и правая границы отрезков, за которые "отвечает" наша вершина, причем левые границы обоих отрезков - включительно, а правые - нет. В дальнейшем будем называть подобную структуру полуинтвервалом.
  
 
Запустим рекурсивную процедуру от всего отрезка(то есть от корневой вершины).
 
Запустим рекурсивную процедуру от всего отрезка(то есть от корневой вершины).
Строка 16: Строка 15:
 
Для текущего состояния проверяем следующие условия :
 
Для текущего состояния проверяем следующие условия :
  
* Если текущий отрезок не пересекается с искомым, то возвращаем нулевое значение.
+
* Если текущий полуинтервал не пересекается с искомым, то возвращаем нулевое значение.
''Например'': текущий <tex>[1..2]</tex>, а искомый <tex>[3 .. 4]</tex>;
+
''Например'': текущий <tex>[1..3)</tex>, а искомый <tex>[3 .. 5)</tex>;
  
* Текущий отрезок совпадает, то возвращаем значение в текущей вершине.
+
* Текущий полуинтервал совпадает, то возвращаем значение в текущей вершине.
''Например'': текущий и искомый <tex>[2..3]</tex>;
+
''Например'': текущий и искомый <tex>[2..4)</tex>;
  
* Иначе переходим к рекурсивным вызовам функций от детей вершины. При этом сумма на текущем отрезке равна сумме результатов функций, запущенных от детей.  
+
* Иначе переходим к рекурсивным вызовам функций от детей вершины. При этом в зависимости от типа запроса возвращаем значение на текущем отрезке, как некоторую функцию от результатов выполнения на детях.
 
''Замечание:''
 
''Замечание:''
  
При передаче новых параметров следует изменять не только границы, за которые отвечает текущая вершина, но и границы запрашиваемого отрезка, чтобы на последующих шагах произошло полное совпадение отрезков.
+
При передаче новых параметров следует изменять не только границы, за которые отвечает текущая вершина, но и границы запрашиваемого полуинтервала, чтобы на последующих шагах произошло полное совпадение полуинтервалов.
  
Так как каждый отрезок разбивается не более, чем на <tex>O(\log n)</tex> отрезков (поскольку на каждом уровне дерева может быть не более двух отрезков из разбиения, а всего уровней <tex>\log n</tex> ), то данная реализация выполняется за <tex>O(\log n)</tex>.
+
Так как каждый полуинтервал разбивается не более, чем на <tex>O(\log n)</tex> полуинтервал (поскольку на каждом уровне дерева может быть не более двух полуинтервалов из разбиения, а всего уровней <tex>\log n</tex> ), то данная реализация выполняется за <tex>O(\log n)</tex>.
  
[[Файл:Шагал1538.JPG|right|380px|thumb|Дерево отрезков]]
 
  
'''Например''' рассмотрим работу программы на дереве отрезков для элементов <tex>[1 .. 8]</tex>.
+
==Пример==
Пусть запрашиваемая сумма - это отрезок <tex>[2 .. 5]</tex>.
+
Рассмотрим данный алгоритм на примере задачи RSQ(запрос суммы на отрезке).
 +
 
 +
Рассмотрим работу алгоритма для вычисления суммы на отрезке. Пусть дерево содержит 8 листьев и запрашиваемая сумма - это отрезок <tex>[1 .. 4]</tex>( полуинтервал <tex>[1 .. 5)</tex>)
  
*Текущий отрезок <tex>[1 .. 8]</tex>, он больше <tex>[2 .. 5]</tex> => переходим по рекурсивным вызовам на <tex>[1 .. 4]</tex>  и <tex>[5 .. 8]</tex>
+
*Текущий полуинтервал <tex>[0 .. 8)</tex>, он больше <tex>[1 .. 5)</tex> => переходим по рекурсивным вызовам на <tex>[0 .. 4)</tex>  и <tex>[4 .. 8)</tex>
  
  
*<tex>[1 .. 4]</tex> выходит за границы<tex> [2 .. 5]</tex>, <tex>[5 .. 8]</tex> выходит за границы <tex>[2 .. 5]</tex> => переходим по рекурсивным вызовам на <tex>[1 .. 2]</tex>, <tex>[3 .. 4]</tex> и <tex>[5 .. 6]</tex>, <tex>[7 .. 8]</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>[1 .. 2]</tex> выходит за границы <tex>[2 .. 5]</tex> => переходим в листья 1, 2;  <tex>[3 .. 4]</tex> целиком внутри <tex>[2 .. 5]</tex> => возвращаем значение в <tex>[3 .. 4]</tex>; <tex>[7 .. 8]</tex> не пересекается с <tex>[2 .. 5]</tex> => возвращаем нулевое значение, <tex>[5 .. 6]</tex> выходит за границы <tex>[2 .. 5]</tex> => переходим к листьям <tex>5</tex> и <tex>6</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>6</tex> не пересекается с отрезком <tex>[2 .. 5]</tex> => возвращаем нулевое значение, лист <tex>5</tex> целиков внутри <tex>[2 .. 5]</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

Данная рекурсивная операция позволяет выполнять запросы на дереве отрезков, причем алгоритм запускается от корня и рекурсивно идет сверху вниз.

Алгоритм

Пусть есть уже построенное дерево отрезков и идет запрос на отрезке [math][a .. b][/math].

Пример дерева отрезков

Будем передавать в качестве параметров рекурсий следующие переменные:

  • [math]node[/math] — номер(в массиве с деревом отрезков) текущей вершины дерева.
  • [math]a[/math], [math]b[/math] — левая и правая границы запрашиваемого отрезка.

Пусть [math]l[/math], [math]r[/math] — это левая и правая границы отрезков, за которые "отвечает" наша вершина, причем левые границы обоих отрезков - включительно, а правые - нет. В дальнейшем будем называть подобную структуру полуинтвервалом.

Запустим рекурсивную процедуру от всего отрезка(то есть от корневой вершины).

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

  • Если текущий полуинтервал не пересекается с искомым, то возвращаем нулевое значение.

Например: текущий [math][1..3)[/math], а искомый [math][3 .. 5)[/math];

  • Текущий полуинтервал совпадает, то возвращаем значение в текущей вершине.

Например: текущий и искомый [math][2..4)[/math];

  • Иначе переходим к рекурсивным вызовам функций от детей вершины. При этом в зависимости от типа запроса возвращаем значение на текущем отрезке, как некоторую функцию от результатов выполнения на детях.

Замечание:

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

Так как каждый полуинтервал разбивается не более, чем на [math]O(\log n)[/math] полуинтервал (поскольку на каждом уровне дерева может быть не более двух полуинтервалов из разбиения, а всего уровней [math]\log n[/math] ), то данная реализация выполняется за [math]O(\log n)[/math].


Пример

Рассмотрим данный алгоритм на примере задачи RSQ(запрос суммы на отрезке).

Рассмотрим работу алгоритма для вычисления суммы на отрезке. Пусть дерево содержит 8 листьев и запрашиваемая сумма - это отрезок [math][1 .. 4][/math]( полуинтервал [math][1 .. 5)[/math])

  • Текущий полуинтервал [math][0 .. 8)[/math], он больше [math][1 .. 5)[/math] => переходим по рекурсивным вызовам на [math][0 .. 4)[/math] и [math][4 .. 8)[/math]


  • [math][0 .. 4)[/math] выходит за границы[math] [1 .. 5)[/math], [math][4 .. 8)[/math] выходит за границы [math][1 .. 5)[/math] => переходим по рекурсивным вызовам на [math][0 .. 2)[/math], [math][2 .. 4)[/math] и [math][4 .. 6)[/math], [math][6 .. 8)[/math].


  • [math][0 .. 2)[/math] выходит за границы [math][1 .. 5)[/math] => переходим в листья 0, 1; [math][2 .. 4)[/math] целиком внутри [math][1 .. 5)[/math] => возвращаем значение в [math][2 .. 4)[/math]; [math][6 .. 8)[/math] не пересекается с [math][1 .. 5)[/math] => возвращаем нулевое значение, [math][4 .. 6)[/math] выходит за границы [math][1 .. 5)[/math] => переходим к листьям [math]4[/math] и [math]5[/math]
  • листья [math]5[/math] и [math]0[/math] не пересекается с полуинтервалом [math][1 .. 5)[/math] => возвращаем нулевое значение, листья [math]4[/math] и [math]1[/math] целиков внутри [math][1 .. 5)[/math] => возвращаем значения в этих листьях.

Реализация

Рассмотрим реализацию рассматриваемой выше задачи RSQ.

 int get_sum (int node, int a, int b)
 {
       // Рассматриваем в реализаций полуинтервалы 
       
       l = tree[node].left;
       r = tree[node].right; 
       if [l, r) [math]\bigcap[/math] [a, b) == [math] \varnothing[/math]
           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);
 } 

Ссылки

MAXimal :: algo :: Дерево отрезков

Дерево отрезков — Википедия

Визуализатор дерева отрезков