Многомерное дерево отрезков — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
м
(вместо + некоторая абстрактная операция, осмысленные имена переменных)
Строка 5: Строка 5:
  
 
==Двумерное дерево отрезков==
 
==Двумерное дерево отрезков==
Поскольку в большинстве случаев на практике используются деревья отрезков размерности не более 2, а также для облегчения понимания далее будут рассматриваться только двумерные деревья отрезков. Рассмотрим следующую задачу.
+
Рассмотрим следующую задачу.
 
{{Задача
 
{{Задача
 
|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> хранится сумма всех элементов.
+
На рисунке справа показан пример дерева отрезков для суммы на массиве 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 - массив дерева отрезков
  // [lx, rx) - полуинтервал
+
  // [leftX, rightX) - полуинтервал
 
   
 
   
  buildX(vx, lx, rx, a[][], t[][])
+
  buildX(vertexX, leftX, rightX, a[][], t[][])
   if lx != rx
+
   if leftX != rightX
     mx = (lx + rx + 1) / 2
+
     mx = (leftX + rightX + 1) / 2
     buildX(vx * 2 + 1, lx, mx, a, t)
+
     buildX(vertexX * 2 + 1, leftX, mx, a, t)
     buildX(vx * 2 + 2, mx, rx, a, t)
+
     buildX(vertexX * 2 + 2, mx, rightX, a, t)
   buildY(vx, lx, rx, 0, 0, m, a, t)
+
   buildY(vertexX, leftX, rightX, 0, 0, m, a, t)
 
   
 
   
  buildY(vx, lx, rx, vy, ly, ry, a[][], t[][])
+
  buildY(vertexX, leftX, rightX, vertexY, leftY, rightY, a[][], t[][])
   if ly == ry
+
   if leftY == rightY
     if lx == rx
+
     if leftX == rightX
       t[vx][vy] = a[lx][ly]  
+
       t[vertexX][vertexY] = a[leftX][leftY]  
 
     else
 
     else
       t[vx][vy] = t[vx * 2 + 1][vy] + t[vx * 2 + 2][vy]
+
       t[vertexX][vertexY] = t[vertexX * 2 + 1][vertexY] <tex>\times</tex> t[vertexX * 2 + 2][vertexY]
 
   else
 
   else
     my = (ly + ry + 1) / 2
+
     my = (leftY + rightY + 1) / 2
     buildY(vx, lx, rx, vy * 2 + 1, ly, my, a, t)
+
     buildY(vertexX, leftX, rightX, vertexY * 2 + 1, leftY, my, a, t)
     buildY(vx, lx, rx, vy * 2 + 2, my, ry, a, t)
+
     buildY(vertexX, leftX, rightX, vertexY * 2 + 2, my, rightY, a, t)
     t[vx][vy] = t[vx][vy * 2 + 1] + t[vx][vy * 2 + 2]
+
     t[vertexX][vertexY] = t[vertexX][vertexY * 2 + 1] <tex>\times</tex> t[vertexX][vertexY * 2 + 2]
  
 
Подсчет суммы элементов:
 
Подсчет суммы элементов:
  // first call - sumX(0, 0, n - 1, lx, rx, ly, ry, t)
+
  // first call - sumX(0, 0, n - 1, leftX, rightX, leftY, rightY, t)
 
   
 
   
  sumX(vx, tlx, trx, lx, rx, ly, ry, t[][])
+
  sumX(vertexX, leftBorderX, rightBorderX, leftX, rightX, leftY, rightY, t[][])
   if lx > rx
+
   if leftX > rightX
 
   return 0
 
   return 0
   if lx == tlx && rx == trx
+
   if leftX == leftBorderX && rightX == rightBorderX
   return sumY(vx, 0, 0, m - 1, ly, ry)
+
   return sumY(vertexX, 0, 0, m - 1, leftY, rightY)
   tmx = (tlx + trx) / 2
+
   tmx = (leftBorderX + rightBorderX) / 2
   return sumX(vx * 2 + 1, tlx, tmx, lx, min(rx, tmx), ly, ry, t) +
+
   return sumX(vertexX * 2 + 1, leftBorderX, tmx, leftX, min(rightX, tmx), leftY, rightY, t) <tex>\times</tex>
         sumX(vx * 2 + 2, tmx + 1, trx, max(lx, tmx + 1), rx, ly, ry, t)
+
         sumX(vertexX * 2 + 2, tmx + 1, rightBorderX, max(leftX, tmx + 1), rightX, leftY, rightY, t)
 
   
 
   
  sumY(vx, vy, tly, try, ly, ry)
+
  sumY(vertexX, vertexY, leftBorderY, rightBorderY, leftY, rightY)
   if ly > ry
+
   if leftY > rightY
 
   return 0
 
   return 0
   if ly == tly && ry == try
+
   if leftY == leftBorderY && rightY == rightBorderY
   return t[vx][vy]
+
   return t[vertexX][vertexY]
   tmy = (tly + try) / 2
+
   tmy = (leftBorderY + rightBorderY) / 2
   return sumY(vx, vy * 2 + 1, tly, tmy, ly, min(ry, tmy), t) +
+
   return sumY(vertexX, vertexY * 2 + 1, leftBorderY, tmy, leftY, min(rightY, tmy), t) <tex>\times</tex>
         sumY(vx, vy * 2 + 2, tmy + 1, try, max(ly, tmy + 1), ry, 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)
 
  // first call - updateX(0, 0, n - 1, x, y, val, t)
 
   
 
   
  updateX(vx, lx, rx, x, y, val, t[][])
+
  updateX(vertexX, leftX, rightX, x, y, val, t[][])
   if lx != rx
+
   if leftX != rightX
   mx = (lx + rx) / 2
+
   mx = (leftX + rightX) / 2
 
   if x <= mx
 
   if x <= mx
     updateX(vx * 2 + 1, lx, mx, x, y, val, t)
+
     updateX(vertexX * 2 + 1, leftX, mx, x, y, val, t)
 
   else
 
   else
     updateX(vx * 2 + 2, mx + 1, rx, x, y, val, t)
+
     updateX(vertexX * 2 + 2, mx + 1, rightX, x, y, val, t)
   updateY(vx, lx, rx, 0, 0, m - 1, x, y, val, t)
+
   updateY(vertexX, leftX, rightX, 0, 0, m - 1, x, y, val, t)
 
   
 
   
  updateY(vx, lx, rx, vy, ly, ry, x, y, val, t[][])
+
  updateY(vertexX, leftX, rightX, vertexY, leftY, rightY, x, y, val, t[][])
   if ly == ry
+
   if leftY == rightY
   if lx == rx
+
   if leftX == rightX
     t[vx][vy] = val
+
     t[vertexX][vertexY] = val
 
   else
 
   else
     t[vx][vy] = t[vx * 2 + 1][vy] + t[vx * 2 + 2][vy]
+
     t[vertexX][vertexY] = t[vertexX * 2 + 1][vertexY] <tex>\times</tex> t[vertexX * 2 + 2][vertexY]
 
   else
 
   else
   my = (ly + ry) / 2
+
   my = (leftY + rightY) / 2
 
   if y <= my
 
   if y <= my
     updateY(vx, lx, rx, vy * 2 + 1, ly, my, x, y, val, t)
+
     updateY(vertexX, leftX, rightX, vertexY * 2 + 1, leftY, my, x, y, val, t)
 
   else
 
   else
     updateY(vx, lx, rx, vy * 2 + 2, my + 1, ry, x, y, val, t)  
+
     updateY(vertexX, leftX, rightX, vertexY * 2 + 2, my + 1, rightY, x, y, val, t)  
   t[vx][vy] = t[vx][vy * 2 + 1] + t[vx][vy * 2 + 2]   
+
   t[vertexX][vertexY] = t[vertexX][vertexY * 2 + 1] <tex>\times</tex> t[vertexX][vertexY * 2 + 2]   
  
  

Версия 15:30, 25 мая 2012

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

Структура

[math]n[/math]-мерное дерево отрезков — обычное дерево отрезков, элементами которого являются деревья отрезков размерности на 1 меньше. Основная идея заключается в рекурсивном переходе к деревьям меньшей размерности. Рассмотрим работу этого принципа на следующем примере. Пусть задано [math]p[/math]-мерное пространство с координатными осями [math]x_1, x_2, x_3...x_p[/math]. Необходимо вычислять некоторую ассоциативную функцию на гиперпрямоугольнике. Для этого сначала найдем элементы дерева, соответствующие [math]x_1[/math] координате. Для каждого из этих элементов рекурсивно перейдем в соответствующие им деревья отрезков и в них найдем элементы, отвечающие соответствующим координатам [math]x_2[/math] исходной гиперпрямоугольной области, и т. д. Таким образом, алгоритм совершит [math]p[/math] вхождений в рекурсию, каждая итерация которой работает за [math]O(\log n)[/math] и получим необходимую асимптотику.

Двумерное дерево отрезков

Рассмотрим следующую задачу.

Задача:
Дано поле размером [math]N \times M[/math], заполненное некоторыми числами. Необходимо уметь обрабатывать два типа запросов:
  • Изменить некоторый элемент
  • Посчитать значение некоторой ассоциативной функции [math]\times[/math] на прямоугольной области


Хранение

Пример двумерного дерева отрезков для 16 элементов

Двумерное дерево отрезков удобно хранить в виде массива, размером [math]4N \times 4M[/math]. Каждая строчка такого массива соответствует некоторому отрезку по первой координате. Сама же строчка является деревом отрезков по второй координате.

На рисунке справа показан пример дерева отрезков для суммы на массиве 4 на 4, заполненного числами от 1 от 16. Например, в элементе [math]a[2][0] = 100[/math] хранится сумма элементов, соответствующих отрезку [math][2..3][/math] по первой координате и [math][0..3][/math] по второй в исходном массиве. А в ячейке [math]a[0][0] = 136[/math] хранится сумма всех элементов.

Интересно, что если построить дерево вначале по второй координате, а потом по первой, то получившийся массив будет таким же. Т. е. данный двумерный массив можно рассматривать как массив деревьев отрезков, где каждое дерево соответствует некоторому отрезку по второй координате, а в нем хранятся суммы по первой.

Заметим, что в общем случае для хранения [math]p[/math]-мерного дерева отрезков требуется [math]4^p n[/math] памяти, где [math]n[/math] — общее количество элементов.

Псевдокод для двумерного случая

Построение:

// 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] [math]\times[/math] 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] [math]\times[/math] 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) [math]\times[/math]
        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) [math]\times[/math]
        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] [math]\times[/math] 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] [math]\times[/math] t[vertexX][vertexY * 2 + 2]   


Источники

См. также