Многомерное дерево отрезков — различия между версиями
м |
(вместо + некоторая абстрактная операция, осмысленные имена переменных) |
||
Строка 5: | Строка 5: | ||
==Двумерное дерево отрезков== | ==Двумерное дерево отрезков== | ||
− | + | Рассмотрим следующую задачу. | |
{{Задача | {{Задача | ||
|definition= | |definition= | ||
Дано поле размером <tex>N \times M</tex>, заполненное некоторыми числами. Необходимо уметь обрабатывать два типа запросов: | Дано поле размером <tex>N \times M</tex>, заполненное некоторыми числами. Необходимо уметь обрабатывать два типа запросов: | ||
* Изменить некоторый элемент | * Изменить некоторый элемент | ||
− | * Посчитать | + | * Посчитать значение некоторой ассоциативной функции <tex>\times</tex> на прямоугольной области |
}} | }} | ||
Строка 17: | Строка 17: | ||
Двумерное дерево отрезков удобно хранить в виде массива, размером <tex>4N \times 4M</tex>. Каждая строчка такого массива соответствует некоторому отрезку по первой координате. Сама же строчка является деревом отрезков по второй координате. | Двумерное дерево отрезков удобно хранить в виде массива, размером <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> хранится сумма всех элементов. |
Интересно, что если построить дерево вначале по второй координате, а потом по первой, то получившийся массив будет таким же. Т. е. данный двумерный массив можно рассматривать как массив деревьев отрезков, где каждое дерево соответствует некоторому отрезку по второй координате, а в нем хранятся суммы по первой. | Интересно, что если построить дерево вначале по второй координате, а потом по первой, то получившийся массив будет таким же. Т. е. данный двумерный массив можно рассматривать как массив деревьев отрезков, где каждое дерево соответствует некоторому отрезку по второй координате, а в нем хранятся суммы по первой. | ||
Строка 23: | Строка 23: | ||
Заметим, что в общем случае для хранения <tex>p</tex>-мерного дерева отрезков требуется <tex>4^p n</tex> памяти, где <tex>n</tex> {{---}} общее количество элементов. | Заметим, что в общем случае для хранения <tex>p</tex>-мерного дерева отрезков требуется <tex>4^p n</tex> памяти, где <tex>n</tex> {{---}} общее количество элементов. | ||
− | ==Псевдокод== | + | ==Псевдокод для двумерного случая== |
Построение: | Построение: | ||
// first call - buildX(0, 0, n, a, t) | // first call - buildX(0, 0, n, a, t) | ||
// a - исходный массив | // a - исходный массив | ||
// t - массив дерева отрезков | // t - массив дерева отрезков | ||
− | // [ | + | // [leftX, rightX) - полуинтервал |
− | buildX( | + | buildX(vertexX, leftX, rightX, a[][], t[][]) |
− | if | + | if leftX != rightX |
− | mx = ( | + | mx = (leftX + rightX + 1) / 2 |
− | buildX( | + | buildX(vertexX * 2 + 1, leftX, mx, a, t) |
− | buildX( | + | buildX(vertexX * 2 + 2, mx, rightX, a, t) |
− | buildY( | + | buildY(vertexX, leftX, rightX, 0, 0, m, a, t) |
− | buildY( | + | buildY(vertexX, leftX, rightX, vertexY, leftY, rightY, a[][], t[][]) |
− | if | + | if leftY == rightY |
− | if | + | if leftX == rightX |
− | t[ | + | t[vertexX][vertexY] = a[leftX][leftY] |
else | else | ||
− | t[ | + | t[vertexX][vertexY] = t[vertexX * 2 + 1][vertexY] <tex>\times</tex> t[vertexX * 2 + 2][vertexY] |
else | else | ||
− | my = ( | + | my = (leftY + rightY + 1) / 2 |
− | buildY( | + | buildY(vertexX, leftX, rightX, vertexY * 2 + 1, leftY, my, a, t) |
− | buildY( | + | buildY(vertexX, leftX, rightX, vertexY * 2 + 2, my, rightY, a, t) |
− | t[ | + | t[vertexX][vertexY] = t[vertexX][vertexY * 2 + 1] <tex>\times</tex> t[vertexX][vertexY * 2 + 2] |
Подсчет суммы элементов: | Подсчет суммы элементов: | ||
− | // first call - sumX(0, 0, n - 1, | + | // first call - sumX(0, 0, n - 1, leftX, rightX, leftY, rightY, t) |
− | sumX( | + | sumX(vertexX, leftBorderX, rightBorderX, leftX, rightX, leftY, rightY, t[][]) |
− | if | + | if leftX > rightX |
return 0 | return 0 | ||
− | if | + | if leftX == leftBorderX && rightX == rightBorderX |
− | return sumY( | + | return sumY(vertexX, 0, 0, m - 1, leftY, rightY) |
− | tmx = ( | + | tmx = (leftBorderX + rightBorderX) / 2 |
− | return sumX( | + | return sumX(vertexX * 2 + 1, leftBorderX, tmx, leftX, min(rightX, tmx), leftY, rightY, t) <tex>\times</tex> |
− | sumX( | + | sumX(vertexX * 2 + 2, tmx + 1, rightBorderX, max(leftX, tmx + 1), rightX, leftY, rightY, t) |
− | sumY( | + | sumY(vertexX, vertexY, leftBorderY, rightBorderY, leftY, rightY) |
− | if | + | if leftY > rightY |
return 0 | return 0 | ||
− | if | + | if leftY == leftBorderY && rightY == rightBorderY |
− | return t[ | + | return t[vertexX][vertexY] |
− | tmy = ( | + | tmy = (leftBorderY + rightBorderY) / 2 |
− | return sumY( | + | return sumY(vertexX, vertexY * 2 + 1, leftBorderY, tmy, leftY, min(rightY, tmy), t) <tex>\times</tex> |
− | sumY( | + | sumY(vertexX, vertexY * 2 + 2, tmy + 1, rightBorderY, max(leftY, tmy + 1), rightY, t) |
Обновление элемента: | Обновление элемента: | ||
// first call - updateX(0, 0, n - 1, x, y, val, t) | // first call - updateX(0, 0, n - 1, x, y, val, t) | ||
− | updateX( | + | updateX(vertexX, leftX, rightX, x, y, val, t[][]) |
− | if | + | if leftX != rightX |
− | mx = ( | + | mx = (leftX + rightX) / 2 |
if x <= mx | if x <= mx | ||
− | updateX( | + | updateX(vertexX * 2 + 1, leftX, mx, x, y, val, t) |
else | else | ||
− | updateX( | + | updateX(vertexX * 2 + 2, mx + 1, rightX, x, y, val, t) |
− | updateY( | + | updateY(vertexX, leftX, rightX, 0, 0, m - 1, x, y, val, t) |
− | updateY( | + | updateY(vertexX, leftX, rightX, vertexY, leftY, rightY, x, y, val, t[][]) |
− | if | + | if leftY == rightY |
− | if | + | if leftX == rightX |
− | t[ | + | t[vertexX][vertexY] = val |
else | else | ||
− | t[ | + | t[vertexX][vertexY] = t[vertexX * 2 + 1][vertexY] <tex>\times</tex> t[vertexX * 2 + 2][vertexY] |
else | else | ||
− | my = ( | + | my = (leftY + rightY) / 2 |
if y <= my | if y <= my | ||
− | updateY( | + | updateY(vertexX, leftX, rightX, vertexY * 2 + 1, leftY, my, x, y, val, t) |
else | else | ||
− | updateY( | + | updateY(vertexX, leftX, rightX, vertexY * 2 + 2, my + 1, rightY, x, y, val, t) |
− | t[ | + | t[vertexX][vertexY] = t[vertexX][vertexY * 2 + 1] <tex>\times</tex> t[vertexX][vertexY * 2 + 2] |
Версия 15:30, 25 мая 2012
Дерево отрезков естественным образом обобщается на двумерный и вообще говоря многомерный случай. Такая структура данных может вычислять значение некоторой ассоциативной функции на гиперпрямоугольнике за , где — размерность пространства, а — ширина гиперкуба на котором производятся вычисления.
Содержание
Структура
-мерное дерево отрезков — обычное дерево отрезков, элементами которого являются деревья отрезков размерности на 1 меньше. Основная идея заключается в рекурсивном переходе к деревьям меньшей размерности. Рассмотрим работу этого принципа на следующем примере. Пусть задано -мерное пространство с координатными осями . Необходимо вычислять некоторую ассоциативную функцию на гиперпрямоугольнике. Для этого сначала найдем элементы дерева, соответствующие координате. Для каждого из этих элементов рекурсивно перейдем в соответствующие им деревья отрезков и в них найдем элементы, отвечающие соответствующим координатам исходной гиперпрямоугольной области, и т. д. Таким образом, алгоритм совершит вхождений в рекурсию, каждая итерация которой работает за и получим необходимую асимптотику.
Двумерное дерево отрезков
Рассмотрим следующую задачу.
Задача: |
Дано поле размером
| , заполненное некоторыми числами. Необходимо уметь обрабатывать два типа запросов:
Хранение
Двумерное дерево отрезков удобно хранить в виде массива, размером
. Каждая строчка такого массива соответствует некоторому отрезку по первой координате. Сама же строчка является деревом отрезков по второй координате.На рисунке справа показан пример дерева отрезков для суммы на массиве 4 на 4, заполненного числами от 1 от 16. Например, в элементе
хранится сумма элементов, соответствующих отрезку по первой координате и по второй в исходном массиве. А в ячейке хранится сумма всех элементов.Интересно, что если построить дерево вначале по второй координате, а потом по первой, то получившийся массив будет таким же. Т. е. данный двумерный массив можно рассматривать как массив деревьев отрезков, где каждое дерево соответствует некоторому отрезку по второй координате, а в нем хранятся суммы по первой.
Заметим, что в общем случае для хранения
-мерного дерева отрезков требуется памяти, где — общее количество элементов.Псевдокод для двумерного случая
Построение:
// first call - buildX(0, 0, n, a, t) // a - исходный массив // t - массив дерева отрезков // [leftX, rightX) - полуинтервал buildX(vertexX, leftX, rightX, a[][], t[][]) if leftX != rightX mx = (leftX + rightX + 1) / 2 buildX(vertexX * 2 + 1, leftX, mx, a, t) buildX(vertexX * 2 + 2, mx, rightX, a, t) buildY(vertexX, leftX, rightX, 0, 0, m, a, t) buildY(vertexX, leftX, rightX, vertexY, leftY, rightY, a[][], t[][]) if leftY == rightY if leftX == rightX t[vertexX][vertexY] = a[leftX][leftY] else t[vertexX][vertexY] = t[vertexX * 2 + 1][vertexY]t[vertexX * 2 + 2][vertexY] else my = (leftY + rightY + 1) / 2 buildY(vertexX, leftX, rightX, vertexY * 2 + 1, leftY, my, a, t) buildY(vertexX, leftX, rightX, vertexY * 2 + 2, my, rightY, a, t) t[vertexX][vertexY] = t[vertexX][vertexY * 2 + 1] t[vertexX][vertexY * 2 + 2]
Подсчет суммы элементов:
// first call - sumX(0, 0, n - 1, leftX, rightX, leftY, rightY, t) sumX(vertexX, leftBorderX, rightBorderX, leftX, rightX, leftY, rightY, t[][]) if leftX > rightX return 0 if leftX == leftBorderX && rightX == rightBorderX return sumY(vertexX, 0, 0, m - 1, leftY, rightY) tmx = (leftBorderX + rightBorderX) / 2 return sumX(vertexX * 2 + 1, leftBorderX, tmx, leftX, min(rightX, tmx), leftY, rightY, t)sumX(vertexX * 2 + 2, tmx + 1, rightBorderX, max(leftX, tmx + 1), rightX, leftY, rightY, t) sumY(vertexX, vertexY, leftBorderY, rightBorderY, leftY, rightY) if leftY > rightY return 0 if leftY == leftBorderY && rightY == rightBorderY return t[vertexX][vertexY] tmy = (leftBorderY + rightBorderY) / 2 return sumY(vertexX, vertexY * 2 + 1, leftBorderY, tmy, leftY, min(rightY, tmy), t) sumY(vertexX, vertexY * 2 + 2, tmy + 1, rightBorderY, max(leftY, tmy + 1), rightY, t)
Обновление элемента:
// first call - updateX(0, 0, n - 1, x, y, val, t) updateX(vertexX, leftX, rightX, x, y, val, t[][]) if leftX != rightX mx = (leftX + rightX) / 2 if x <= mx updateX(vertexX * 2 + 1, leftX, mx, x, y, val, t) else updateX(vertexX * 2 + 2, mx + 1, rightX, x, y, val, t) updateY(vertexX, leftX, rightX, 0, 0, m - 1, x, y, val, t) updateY(vertexX, leftX, rightX, vertexY, leftY, rightY, x, y, val, t[][]) if leftY == rightY if leftX == rightX t[vertexX][vertexY] = val else t[vertexX][vertexY] = t[vertexX * 2 + 1][vertexY]t[vertexX * 2 + 2][vertexY] else my = (leftY + rightY) / 2 if y <= my updateY(vertexX, leftX, rightX, vertexY * 2 + 1, leftY, my, x, y, val, t) else updateY(vertexX, leftX, rightX, vertexY * 2 + 2, my + 1, rightY, x, y, val, t) t[vertexX][vertexY] = t[vertexX][vertexY * 2 + 1] t[vertexX][vertexY * 2 + 2]