47
правок
Изменения
м
'''function''' treeBuild(a[], i, tl, tr): <font color="green">// Мы находимся в вершине с номером i, который отвечает за полуинтервал [tl, tr)</font>
Пример дерева отрезков для операции минимум:
* [[http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81%D0%B0_%D0%B2_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2_%D1%81%D0%B2%D0%B5%D1%80%D1%85%D1%83 - Реализация запроса в дереве запросов к дереву отрезков сверху]]
* [[http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81%D0%B0_%D0%B2_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%B5_%D0%BE%D1%82%D1%80%D0%B5%D0%B7%D0%BA%D0%BE%D0%B2_%D1%81%D0%BD%D0%B8%D0%B7%D1%83 - Реализация запроса в дереве запросов к дереву отрезков снизу]]
* [[Несогласованные поддеревьяhttp://neerc.ifmo.ru/wiki/index.php?title=%D0%9D%D0%B5%D1%81%D0%BE%D0%B3%D0%BB%D0%B0%D1%81%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5_%D0%BF%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D1%8C%D1%8F. _%D0%A0%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%BC%D0%B0%D1%81%D1%81%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE_%D0%BE%D0%B1%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F - Реализация массового обновления]]
* [[wikipediahttp://rain.ifmo.ru:Дерево отрезков|Википедия {{/cat/view.php/vis/trees/segment-2006 --}} Дерево Визуализатор дерева отрезков]] * [[wikipedia:Segment tree|Wikipedia {{---}} Segment tree]]
* [[wikipediahttp://e-maxx.ru/algo/segment_tree - MAXimal :Моноид|Википедия {{---}} Моноид]: algo :: Дерево отрезков]
* [http://rainru.ifmo.ru/cat/viewwikipedia.php/visorg/treeswiki/segment%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 -2006 Визуализатор дерева Дерево отрезков— Википедия]
* [http://e-maxxru.wikipedia.ruorg/algowiki/segment_tree MAXimal :: algo :: Дерево отрезковМоноид - Моноид — Википедия]
Чуть более очевидная версия построения снизу
'''Дерево отрезков''' (англ. ''Segment tree'') {{---}} это структура данных, которая позволяет за асимптотику <tex>O(\log n)</tex> реализовать любые операции, определяемые на [[Моноид | моноиде]]. Например, следующего вида: нахождение суммы (задача RSQ), минимума или максимума (задача RMQ) элементов массива в заданном отрезке (<tex>a[l...r]</tex>,a[где <tex>l+1],\dots,a[</tex> и <tex>r]</tex>.поступают на вход алгоритма)
При этом дополнительно возможно изменение элементов массива: как изменение значения одного элемента, так и [[Несогласованные поддеревья. Реализация массового обновления | изменение элементов на целом подотрезке массива]], например разрешается присвоить всем элементам <tex>a[l \dots ...r]</tex> какое-либо значение, либо прибавить ко всем элементам массива какое-либо число. Структура занимает <tex>O(n)</tex> памяти, а ее построение требует <tex>O(n)</tex> времени.
==Структура==
Структура представляет собой дерево, листьями которого являются элементы исходного массива. Другие вершины этого дерева имеют по два 2 ребенка и содержат результат операции от своих детей (например минимум или сумму). Таким образом, корень содержит результат искомой функции от всего массива <tex>[0 \dots ...n-1]</tex>, левый ребёнок корня содержит результат функции на <tex dpi=120>[0\dots...\frac{n}{2}]</tex>, а правый, соответственно результат на <tex dpi=120>[\frac{n}{2}+1 \dots ...n-1]</tex>. И так далее, продвигаясь вглубь дерева.
==Построение дерева==
Пусть исходный массив <tex>a</tex> состоит из <tex>n</tex> элементов. Для удобства построения увеличим длину массива <tex>a</tex> так, чтобы она равнялась ближайшей степени двойки, т.е. <tex>2^k</tex>, где <tex>2^k \geqslant ge n</tex>. Это сделано, для того чтобы не допустить обращение к несуществующим элементам массива при дальнейшем процессе построения. Пустые элементы необходимо заполнить нейтральными элементами моноида. Тогда для хранения дерева отрезков понадобится массив <tex>t</tex> из <tex>2^{k+1}</tex> элементов, поскольку в худшем случае количество вершин в дереве можно оценить суммой <tex>n+\frac{n}{2}+\frac{n}{4} \dots ...+1 < 2n</tex>, где <tex>n=2^k</tex>. Таким образом, структура занимает линейную память.
Процесс построения дерева заключается в заполнении массива <tex>t</tex>. Заполним этот массив таким образом, чтобы <tex>i</tex>-й элемент являлся бы результатом некоторой бинарной операции <tex>\circ</tex> (для каждой конкретной задачи своей) от элементов c номерами <tex>2i+1</tex> и <tex>2i+2</tex>, то есть родитель являлся результатом бинарной операции от своих сыновей. Один из вариантов — делать рекурсивно. Пусть у нас имеются исходный массив <tex>a</tex>, а также переменные <tex>tl</tex> и <tex>tr</tex>, обозначающие границы текущего полуинтервала <tex>[tl, tr)</tex>. Запускаем процедуру построения от корня дерева отрезков (<tex>i=0</tex>, <tex>tl=0</tex>, <tex>tr=n</tex>), а сама процедура построения, если её вызвали не от листа, вызывает себя от каждого из двух сыновей и суммирует вычисленные значения, а если её вызвали от листа — то просто записывает в себя значение этого элемента массива (Для этого у нас есть исходный массив <tex> a </tex>). Асимптотика построения дерева отрезков составит , таким образом, <tex>O(n)</tex>.
Выделяют два основных способа построения дерева отрезков: построение снизу и построение сверху. При построении [[Реализация запроса в дереве отрезков снизу | снизу]] алгоритм поднимается от листьев к корню (Просто начинаем заполнять элементы массива <tex>t</tex> от большего индекса к меньшему, таким образом при заполнении элемента <tex> i </tex> его дети <tex>2i+1</tex> и <tex>2i+2</tex> уже будут заполнены, и мы с легкостью посчитаем бинарную операцию от них), а при построении [[Реализация запроса в дереве отрезков сверху | сверху ]] спускается от корня к листьям. Особенные изменения появляются в реализации запросов к таким деревьям отрезков.
[[Файл:Segment_tree.png|Пример дерева отрезков для максимума]]
Реализация построения сверху:
'''if''' tl == tr
'''return'''
t[i] = a[tl]
'''else'''
tm = (tl + tr) / 2 <font color="green">//середина отрезка</font> treeBuildTreeBuild(a, 2 * i + 1, tl, tm) treeBuildTreeBuild(a, 2 * i + 2, tm, tr)
t[i] = t[2 * i + 1] <tex> \circ </tex> t[2 * i + 2]
Реализация построения снизу:
<code> '''function''' treeBuild(a[]): '''for''' i = n - 1 0 .. 2 * n - 1 t[n - 1 + i] = a[i - n - 1] '''for''' i = n - 2 .. 0 t[i] = t[2 * i + 1] <tex> \circ </tex> t[2 * i + 2]</code>
==Персистентное дерево отрезков==
===Построение===
Для построения персистентного дерева отрезков из <tex>n</tex> элементов необходимо пройтись по всему деревуприменить <tex>n</tex> раз операцию добавления элемента к последней версии дерева. Для того, попутно создавая все необходимые вершины. В листах создавать вершинучтобы добавить новый элемент к <tex>k</tex>-ой версии дерева, необходимо проверить, хранящую соответствующее значениеявляется ли оно полным бинарным. Промежуточные же вершины строим от левого и правого сыновей '''function''' treeBuild(a[]Если да, tlто создадим новый корень, tr): '''if''' tr - tl == 1 '''return''' Vertex(aлевым сыном сделаем <tex>roots[tlk]) tm = (tl + tr) </ 2 '''return''' Vertex(treeBuild(a[]tex>. Иначе, сделаем копию корня исходной версии. Добавим корень в конец массива корней. Далее, спускаясь от корня к первому свободному листу, будем создавать несуществующие узлы и клонировать существующие. После этого в новой ветке необходимо обновить значение функции и некоторые указатели дочерних элементов. Поэтому, tlвозвращаясь из рекурсии, tm)будем менять один указатель на только что созданную или скопированную вершину, treeBuild(a[]а также обновим значение функции, tmдля которой строилось дерево. После этой операции в дереве появится новая версия, tr))содержащая вставленный элемент.
===Изменение элемента===
Для того, чтобы изменить элемент в персистентном дереве отрезков, необходимо сделать следующие действия: спустимся в дереве от корня нужной версии до требуемого элемента, скопируем его, изменим значение, и, поднимаясь по дереву, будем клонировать узлы. При этом необходимо менять указатель на одного из детей на узел, созданный при предыдущем клонировании. После копирования корня, добавим новый корень в конец массива корней.
[[Файл:persist.png]]
Здесь изображено персистентное дерево отрезков с операцией минимум, в котором изначально было <tex>3</tex> элементавершины. Сперва к нему была добавлена вершина со значением <tex>2</tex>, а потом изменена вершина со значением <tex>7</tex>. Цвет ребер и вершин соответствует времени их появления. Синий цвет элементов означает, что они были изначально, зеленый {{---}} что они появились после добавления, а оранжевый {{---}} что они появились после изменения элемента. '''function''' update(v, tl, tr, pos, val): '''if''' tr - tl == 1 '''return''' Vertex(val) tm = (tl + tr) / 2 '''if''' pos <tex>\leqslant</tex> tm '''return''' Vertex(update(v.l, tl, tm, pos, val), v.r) '''else''' '''return''' Vertex(v.l, update(v.r, tm, tr, pos, val))
==См. также==
==Ссылки==
[[Категория: Дискретная математика и алгоритмы]]
[[Категория: Дерево отрезков]]