10
правок
Изменения
Новая страница: «'''Дерево отрезков''' {{---}} это структура данных, которая позволяет эффективно (за асимптотик…»
'''Дерево отрезков''' {{---}} это структура данных, которая позволяет эффективно (за асимптотику <tex dpi = "140">O(log n))</tex> реализовать операции следующего вида: нахождение суммы (задача RSQ), минимума или максимума (задача RMQ) элементов массива в заданном отрезке (<tex dpi = "140">a[i...j]</tex>, где <tex dpi = "140">i</tex> и <tex dpi = "140">j</tex> поступают на вход алгоритма), при этом дополнительно возможно изменение элементов массива: как изменение значения одного элемента, так и изменение элементов на целом подотрезке массива (т.е. разрешается присвоить всем элементам <tex dpi = "140">a[i...j]</tex> какое-либо значение, либо прибавить ко всем элементам массива какое-либо число). Структура занимает <tex dpi = "140">O(n)</tex> памяти и выстраивается из массива за <tex dpi = "140">O(n)</tex>.
==Структура==
[[Файл:Segment_tree.jpg|right|400px|thumb|Пример дерева отрезков для вычисления сумм]]Структура представляет собой дерево, листьями которого являются элементы исходного массива. Другие вершины этого дерева имеют по 2 ребёнка и содержат сумму, минимум/максимум своих детей. Таким образом, корень содержит результат искомой функцию от всего массива <tex dpi = "140">[0...n]</tex>, левый ребёнок корня содержит результат функции на <tex dpi = "140">[0...n/2]</tex>, а правый, соответственно результат на <tex dpi = "140">[n/2+1...n]</tex>. И так далее, продвигаясь вглубь дерева.
==Построение дерева==
Пусть исходный массив <tex dpi = "140">a</tex> состоит из <tex dpi = "140">n</tex> элементов. Для удобства построения увеличим длину массива <tex dpi = "140">a</tex> так, чтобы она равнялась ближайшей степени двойки, т.е. <tex dpi = "140">2^k</tex>, где <tex dpi = "140">2^k \ge n</tex>. Это сделано, для того чтобы не допустить обращение к несуществующим элементам массива при дальнейшем процессе построения. Пустые элементы можно заполнить нулями или бесконечностями (за бесконечностью стоит понимать, например, число, больше которого в данных ничего не появится) в зависимости от поставленной задачи. Тогда для хранения дерева отрезков понадобится массив <tex dpi = "140">t</tex> из <tex dpi = "140">2^{k+1}</tex> элементов, поскольку в худшем случае количество вершин в дереве можно оценить суммой <tex dpi = "135">n+n/2+n/4...+1 < 2n</tex>, где <tex dpi = "140">n=2^k</tex>. Таким образом, структура занимает линейную память.
Далее будем считать, что дерево выстраиваем для задачи вычисления суммы на отрезке. Для минимума и максимума операция построения проделывается аналогично.
Процесс построения дерева заключается в заполнении массива <tex dpi = "140">t</tex>. Заполним массив таким образом, чтобы <tex dpi = "140">i</tex>-й элемент являлся бы суммой элемента c номером <tex dpi = "140">2i</tex> и элемента с номером <tex dpi = "140">2i+1</tex>, т.е. родитель являлся бы суммой своих сыновей. Лучше всего эту процедуру делать рекурсивно. Создадим функцию от исходного массива <tex dpi = "140">a</tex>, переменной <tex dpi = "140">i</tex>, обозначающей номер элемента в массиве <tex dpi = "140">t</tex>, а так же переменные <tex dpi = "140">tl</tex> и <tex dpi = "140">tr</tex>, обозначающие соответственно левую и правую границы текущего отрезка. Запускаем процедуру построения от корня дерева отрезков (<tex dpi = "140">i=1</tex>, <tex dpi = "140">tl=0</tex>, <tex dpi = "140">tr=n-1</tex>), а сама процедура построения, если её вызвали не от листа, вызывает себя от каждого из двух сыновей и суммирует вычисленные значения, а если её вызвали от листа — то просто записывает в себя значение этого элемента массива. Асимптотика построения дерева отрезков составит, таким образом, <tex dpi = "140">O(n)</tex>.
Реализация:
TreeBuild(a[], i, tl, tr)
if (tl = tr)
t[i] = a[tl];
else
tm = (tl + tr) / 2; //середина отрезка
TreeBuild(a, 2*v, tl, tm);
TreeBuild(a, 2*v+1, tm+1, tr);
t[v] = t[2*v] + t[2*v+1];
==Ссылки==
[http://rain.ifmo.ru/cat/view.php/vis/trees/segment-2006 - Визуализатор дерева отрезков]
[http://e-maxx.ru/algo/segment_tree - MAXimal :: algo :: Дерево отрезков]
[http://ru.wikipedia.org/wiki/%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2 - Дерево отрезков — Википедия]
==Структура==
[[Файл:Segment_tree.jpg|right|400px|thumb|Пример дерева отрезков для вычисления сумм]]Структура представляет собой дерево, листьями которого являются элементы исходного массива. Другие вершины этого дерева имеют по 2 ребёнка и содержат сумму, минимум/максимум своих детей. Таким образом, корень содержит результат искомой функцию от всего массива <tex dpi = "140">[0...n]</tex>, левый ребёнок корня содержит результат функции на <tex dpi = "140">[0...n/2]</tex>, а правый, соответственно результат на <tex dpi = "140">[n/2+1...n]</tex>. И так далее, продвигаясь вглубь дерева.
==Построение дерева==
Пусть исходный массив <tex dpi = "140">a</tex> состоит из <tex dpi = "140">n</tex> элементов. Для удобства построения увеличим длину массива <tex dpi = "140">a</tex> так, чтобы она равнялась ближайшей степени двойки, т.е. <tex dpi = "140">2^k</tex>, где <tex dpi = "140">2^k \ge n</tex>. Это сделано, для того чтобы не допустить обращение к несуществующим элементам массива при дальнейшем процессе построения. Пустые элементы можно заполнить нулями или бесконечностями (за бесконечностью стоит понимать, например, число, больше которого в данных ничего не появится) в зависимости от поставленной задачи. Тогда для хранения дерева отрезков понадобится массив <tex dpi = "140">t</tex> из <tex dpi = "140">2^{k+1}</tex> элементов, поскольку в худшем случае количество вершин в дереве можно оценить суммой <tex dpi = "135">n+n/2+n/4...+1 < 2n</tex>, где <tex dpi = "140">n=2^k</tex>. Таким образом, структура занимает линейную память.
Далее будем считать, что дерево выстраиваем для задачи вычисления суммы на отрезке. Для минимума и максимума операция построения проделывается аналогично.
Процесс построения дерева заключается в заполнении массива <tex dpi = "140">t</tex>. Заполним массив таким образом, чтобы <tex dpi = "140">i</tex>-й элемент являлся бы суммой элемента c номером <tex dpi = "140">2i</tex> и элемента с номером <tex dpi = "140">2i+1</tex>, т.е. родитель являлся бы суммой своих сыновей. Лучше всего эту процедуру делать рекурсивно. Создадим функцию от исходного массива <tex dpi = "140">a</tex>, переменной <tex dpi = "140">i</tex>, обозначающей номер элемента в массиве <tex dpi = "140">t</tex>, а так же переменные <tex dpi = "140">tl</tex> и <tex dpi = "140">tr</tex>, обозначающие соответственно левую и правую границы текущего отрезка. Запускаем процедуру построения от корня дерева отрезков (<tex dpi = "140">i=1</tex>, <tex dpi = "140">tl=0</tex>, <tex dpi = "140">tr=n-1</tex>), а сама процедура построения, если её вызвали не от листа, вызывает себя от каждого из двух сыновей и суммирует вычисленные значения, а если её вызвали от листа — то просто записывает в себя значение этого элемента массива. Асимптотика построения дерева отрезков составит, таким образом, <tex dpi = "140">O(n)</tex>.
Реализация:
TreeBuild(a[], i, tl, tr)
if (tl = tr)
t[i] = a[tl];
else
tm = (tl + tr) / 2; //середина отрезка
TreeBuild(a, 2*v, tl, tm);
TreeBuild(a, 2*v+1, tm+1, tr);
t[v] = t[2*v] + t[2*v+1];
==Ссылки==
[http://rain.ifmo.ru/cat/view.php/vis/trees/segment-2006 - Визуализатор дерева отрезков]
[http://e-maxx.ru/algo/segment_tree - MAXimal :: algo :: Дерево отрезков]
[http://ru.wikipedia.org/wiki/%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2 - Дерево отрезков — Википедия]