Многомерное дерево отрезков — различия между версиями
VVolochay (обсуждение | вклад) (→Хранение) |
(Переписал почти все заново) |
||
| Строка 1: | Строка 1: | ||
| − | [[Дерево отрезков. Построение|Дерево отрезков]] | + | [[Дерево отрезков. Построение|Дерево отрезков]] естественным образом обобщается на двумерный и вообще говоря многомерный случай. Такая структура данных может вычислять значение некоторой [[Ассоциативная_операция|ассоциативной функции]] на гиперпрямоугольнике за <tex>O(\log^{p} n)</tex>, где <tex>p</tex> {{---}} размерность пространства, а <tex>n</tex> {{---}} ширина гиперкуба на котором производятся вычисления. |
| + | |||
| + | ==Структура== | ||
| + | <tex>n</tex>-мерное дерево отрезков {{---}} обычное дерево отрезков, элементами которого являются деревья отрезков размерности на 1 меньше. Основная идея заключается в рекурсивном переходе к деревьям меньшей размерности. Рассмотрим работу этого принципа на следующем примере. Пусть задано <tex>p</tex>-мерное пространство с координатными осями <tex>x_1, x_2, x_3...x_p</tex>. Необходимо вычислять некоторую ассоциативную функцию на гиперпрямоугольнике. Для этого сначала найдем элементы дерева, соответствующие <tex>x_1</tex> координате. Для каждого из этих элементов рекурсивно перейдем в соответствующие им деревья отрезков и в них найдем элементы, отвечающие соответствующим координатам <tex>x_2</tex> исходной гиперпрямоугольной области, и т. д. Таким образом, алгоритм совершит <tex>p</tex> вхождений в рекурсию, каждая итерация которой работает за <tex>O(\log n)</tex> и получим необходимую асимптотику. | ||
| + | |||
| + | ==Двумерное дерево отрезков== | ||
| + | Поскольку в большинстве случаев на практике используются деревья отрезков размерности не более 2, а также для облегчения понимания далее будут рассматриваться только двумерные деревья отрезков. Рассмотрим следующую задачу. | ||
{{Задача | {{Задача | ||
|definition= | |definition= | ||
| − | + | Дано поле размером <tex>N \times M</tex>, заполненное некоторыми числами. Необходимо уметь обрабатывать два типа запросов: | |
| + | * Изменить некоторый элемент | ||
| + | * Посчитать сумму на прямоугольной области | ||
}} | }} | ||
| − | == | + | ==Хранение== |
| − | + | [[Файл:SegmentTree2DExample.png|thumb|250px|right|Пример двумерного дерева отрезков для 16 элементов]] | |
| + | Двумерное дерево отрезков удобно хранить в виде массива, размером <tex>4N \times 4M</tex>. Каждая строчка такого массива соответствует некоторому отрезку по первой координате. Сама же строчка является деревом отрезков по второй координате. | ||
| + | |||
| + | На рисунке справа показан пример дерева отрезков для массива 4 на 4, заполненного числами от 1 от 16. Например, в элементе <tex>a[2][0] = 100</tex> хранится сумма элементов, соответствующих отрезку <tex>[2..3]</tex> по первой координате и <tex>[0..3]</tex> по второй в исходном массиве. А в ячейке <tex>a[0][0] = 136</tex> хранится сумма всех элементов. | ||
| − | + | Интересно, что если построить дерево вначале по второй координате, а потом по первой, то получившийся массив будет таким же. Т. е. данный двумерный массив можно рассматривать как массив деревьев отрезов, где каждое дерево соответствует некоторому отрезку по второй координате, а в нем хранятся суммы по первой. | |
| − | |||
| − | |||
| − | |||
| − | + | Заметим, что в общем случае для хранения <tex>p</tex>-мерного дерева отрезков требуется <tex>4^p n</tex> памяти, где <tex>n</tex> {{---}} общее количество элементов. | |
| − | [[ | + | ==Псевдокод== |
| + | Построение: | ||
| + | // first call - buildX(0, 0, n, a, t) | ||
| + | // a - исходный массив | ||
| + | // t - массив дерева отрезков | ||
| + | // [lx, rx) - полуинтервал | ||
| + | |||
| + | buildX(vx, lx, rx, a[][], t[][]) | ||
| + | if lx != rx | ||
| + | mx = (lx + rx + 1) / 2 | ||
| + | buildX(vx * 2 + 1, lx, mx, a, t) | ||
| + | buildX(vx * 2 + 2, mx, rx, a, t) | ||
| + | buildY(vx, lx, rx, 0, 0, m, a, t); | ||
| + | |||
| + | buildY(vx, lx, rx, vy, ly, ry, a[][], t[][]) | ||
| + | if ly == ry | ||
| + | if lx == rx | ||
| + | t[vx][vy] = a[lx][ly] | ||
| + | else | ||
| + | t[vx][vy] = t[vx * 2 + 1][vy] + t[vx * 2 + 2][vy]; | ||
| + | else | ||
| + | my = (ly + ry + 1) / 2 | ||
| + | buildY(vx, lx, rx, vy * 2 + 1, ly, my, a, t) | ||
| + | buildY(vx, lx, rx, vy * 2 + 2, my, ry, a, t) | ||
| + | t[vx][vy] = t[vx][vy * 2 + 1] + t[vx][vy * 2 + 2] | ||
| − | == | + | Подсчет суммы элементов: |
| − | [[ | + | // first call - sumX(0, 0, n - 1, lx, rx, ly, ry, t) |
| + | |||
| + | sumX(vx, tlx, trx, lx, rx, ly, ry, t[][]) | ||
| + | if lx > rx | ||
| + | return 0 | ||
| + | if lx == tlx && rx == trx | ||
| + | return sumY(vx, 0, 0, m - 1, ly, ry) | ||
| + | tmx = (tlx + trx) / 2 | ||
| + | return sumX(vx * 2 + 1, tlx, tmx, lx, min(rx, tmx), ly, ry, t) + | ||
| + | sumX(vx * 2 + 2, tmx + 1, trx, max(lx, tmx + 1), rx, ly, ry, t) | ||
| + | |||
| + | sumY(vx, vy, tly, try, ly, ry) | ||
| + | if ly > ry | ||
| + | return 0 | ||
| + | if ly == tly && ry == try | ||
| + | return t[vx][vy] | ||
| + | tmy = (tly + try) / 2 | ||
| + | return sumY(vx, vy * 2 + 1, tly, tmy, ly, min(ry, tmy), t) + | ||
| + | sumY(vx, vy * 2 + 2, tmy + 1, try, max(ly, tmy + 1), ry, t) | ||
| − | == | + | Обновление элемента: |
| − | + | // first call - updateX(0, 0, n - 1, x, y, val, t) | |
| − | + | ||
| + | updateX(vx, lx, rx, x, y, val, t[][]) | ||
| + | if lx != rx | ||
| + | mx = (lx + rx) / 2 | ||
| + | if x <= mx | ||
| + | updateX(vx * 2 + 1, lx, mx, x, y, val, t) | ||
| + | else | ||
| + | updateX(vx * 2 + 2, mx + 1, rx, x, y, val, t) | ||
| + | updateY(vx, lx, rx, 0, 0, m - 1, x, y, val, t) | ||
| + | |||
| + | updateY(vx, lx, rx, vy, ly, ry, x, y, val, t[][]) | ||
| + | if ly == ry | ||
| + | if lx == rx | ||
| + | t[vx][vy] = val | ||
| + | else | ||
| + | t[vx][vy] = t[vx * 2 + 1][vy] + t[vx * 2 + 2][vy] | ||
| + | else | ||
| + | my = (ly + ry) / 2 | ||
| + | if y <= my | ||
| + | updateY(vx, lx, rx, vy * 2 + 1, ly, my, x, y, val, t) | ||
| + | else | ||
| + | updateY(vx, lx, rx, vy * 2 + 2, my + 1, ry, x, y, val, t) | ||
| + | t[vx][vy] = t[vx][vy * 2 + 1] + t[vx][vy * 2 + 2] | ||
| − | |||
==Источники== | ==Источники== | ||
| − | * | + | * [http://e-maxx.ru/algo/segment_tree Дерево отрезков {{---}} e-maxx.ru] |
| − | * | + | * [http://habrahabr.ru/post/131072/)/ Двумерное дерево отрезков {{---}} habrahabr.ru] |
==См. также== | ==См. также== | ||
| − | *[[Дерево отрезков. Построение]] | + | *[[Дерево отрезков. Построение]] |
| − | *[[Сжатое многомерное дерево отрезков]] | + | *[[Сжатое многомерное дерево отрезков]] |
| − | *[[Многомерное дерево Фенвика]] | + | *[[Многомерное дерево Фенвика]] |
| + | |||
| + | [[Категория: Дискретная математика и алгоритмы]] | ||
| + | [[Категория: Дерево отрезков]] | ||
Версия 01:31, 25 мая 2012
Дерево отрезков естественным образом обобщается на двумерный и вообще говоря многомерный случай. Такая структура данных может вычислять значение некоторой ассоциативной функции на гиперпрямоугольнике за , где — размерность пространства, а — ширина гиперкуба на котором производятся вычисления.
Структура
-мерное дерево отрезков — обычное дерево отрезков, элементами которого являются деревья отрезков размерности на 1 меньше. Основная идея заключается в рекурсивном переходе к деревьям меньшей размерности. Рассмотрим работу этого принципа на следующем примере. Пусть задано -мерное пространство с координатными осями . Необходимо вычислять некоторую ассоциативную функцию на гиперпрямоугольнике. Для этого сначала найдем элементы дерева, соответствующие координате. Для каждого из этих элементов рекурсивно перейдем в соответствующие им деревья отрезков и в них найдем элементы, отвечающие соответствующим координатам исходной гиперпрямоугольной области, и т. д. Таким образом, алгоритм совершит вхождений в рекурсию, каждая итерация которой работает за и получим необходимую асимптотику.
Двумерное дерево отрезков
Поскольку в большинстве случаев на практике используются деревья отрезков размерности не более 2, а также для облегчения понимания далее будут рассматриваться только двумерные деревья отрезков. Рассмотрим следующую задачу.
| Задача: |
Дано поле размером , заполненное некоторыми числами. Необходимо уметь обрабатывать два типа запросов:
|
Хранение
Двумерное дерево отрезков удобно хранить в виде массива, размером . Каждая строчка такого массива соответствует некоторому отрезку по первой координате. Сама же строчка является деревом отрезков по второй координате.
На рисунке справа показан пример дерева отрезков для массива 4 на 4, заполненного числами от 1 от 16. Например, в элементе хранится сумма элементов, соответствующих отрезку по первой координате и по второй в исходном массиве. А в ячейке хранится сумма всех элементов.
Интересно, что если построить дерево вначале по второй координате, а потом по первой, то получившийся массив будет таким же. Т. е. данный двумерный массив можно рассматривать как массив деревьев отрезов, где каждое дерево соответствует некоторому отрезку по второй координате, а в нем хранятся суммы по первой.
Заметим, что в общем случае для хранения -мерного дерева отрезков требуется памяти, где — общее количество элементов.
Псевдокод
Построение:
// first call - buildX(0, 0, n, a, t)
// a - исходный массив
// t - массив дерева отрезков
// [lx, rx) - полуинтервал
buildX(vx, lx, rx, a[][], t[][])
if lx != rx
mx = (lx + rx + 1) / 2
buildX(vx * 2 + 1, lx, mx, a, t)
buildX(vx * 2 + 2, mx, rx, a, t)
buildY(vx, lx, rx, 0, 0, m, a, t);
buildY(vx, lx, rx, vy, ly, ry, a[][], t[][])
if ly == ry
if lx == rx
t[vx][vy] = a[lx][ly]
else
t[vx][vy] = t[vx * 2 + 1][vy] + t[vx * 2 + 2][vy];
else
my = (ly + ry + 1) / 2
buildY(vx, lx, rx, vy * 2 + 1, ly, my, a, t)
buildY(vx, lx, rx, vy * 2 + 2, my, ry, a, t)
t[vx][vy] = t[vx][vy * 2 + 1] + t[vx][vy * 2 + 2]
Подсчет суммы элементов:
// first call - sumX(0, 0, n - 1, lx, rx, ly, ry, t)
sumX(vx, tlx, trx, lx, rx, ly, ry, t[][])
if lx > rx
return 0
if lx == tlx && rx == trx
return sumY(vx, 0, 0, m - 1, ly, ry)
tmx = (tlx + trx) / 2
return sumX(vx * 2 + 1, tlx, tmx, lx, min(rx, tmx), ly, ry, t) +
sumX(vx * 2 + 2, tmx + 1, trx, max(lx, tmx + 1), rx, ly, ry, t)
sumY(vx, vy, tly, try, ly, ry)
if ly > ry
return 0
if ly == tly && ry == try
return t[vx][vy]
tmy = (tly + try) / 2
return sumY(vx, vy * 2 + 1, tly, tmy, ly, min(ry, tmy), t) +
sumY(vx, vy * 2 + 2, tmy + 1, try, max(ly, tmy + 1), ry, t)
Обновление элемента:
// first call - updateX(0, 0, n - 1, x, y, val, t) updateX(vx, lx, rx, x, y, val, t[][]) if lx != rx mx = (lx + rx) / 2 if x <= mx updateX(vx * 2 + 1, lx, mx, x, y, val, t) else updateX(vx * 2 + 2, mx + 1, rx, x, y, val, t) updateY(vx, lx, rx, 0, 0, m - 1, x, y, val, t) updateY(vx, lx, rx, vy, ly, ry, x, y, val, t[][]) if ly == ry if lx == rx t[vx][vy] = val else t[vx][vy] = t[vx * 2 + 1][vy] + t[vx * 2 + 2][vy] else my = (ly + ry) / 2 if y <= my updateY(vx, lx, rx, vy * 2 + 1, ly, my, x, y, val, t) else updateY(vx, lx, rx, vy * 2 + 2, my + 1, ry, x, y, val, t) t[vx][vy] = t[vx][vy * 2 + 1] + t[vx][vy * 2 + 2]