Пересечение прямоугольника с множеством прямоугольников (PST) — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Новая страница: «== Определение == Priority Search Tree позволяет решать следующую задачу. Даны <tex>n</tex> точек, а такж...»)
(нет различий)

Версия 03:50, 17 января 2014

Определение

Priority Search Tree позволяет решать следующую задачу. Даны [math]n[/math] точек, а также запросы. Каждый запрос характеризуется отрезком по одной координате [math][y_1, y_2][/math], а также числом [math]x_q[/math] по другой координате. Для каждого запроса структура возвращает все точки, которые находятся в отрезке [math][y_1, y_2][/math] по одной координате, и имеют другую координату не больше [math]x_q[/math]. На каждый запрос дерево отвечает за [math]O(\log\,n + k)[/math], где [math]k[/math] — количество точек в ответе.

Как строится?

Дерево строится рекурсивно. Выберем точку с наименьшей [math]x[/math]-координатой (если таких несколько, то из них выберем с наименьшим [math]y[/math]). Все остальные точки отсортируем по [math]y[/math] и разобьем на две равные (количество может отличаться на один) части. Далее будем считать, что все точки первой части имеют координату [math]y[/math] меньше всех точек второй части (с одинаковыми [math]y[/math] поступаем как обычно — сравниваем по [math]x[/math]). Для каждого из двух множеств, если оно не пусто, построим дерево рекурсивно.

Теорема:
PST занимает [math]O(n)[/math] памяти.
Доказательство:
[math]\triangleright[/math]
Каждая точка будет корнем ровно одного дерева. Количество деревьев не превышает количества точек. В сумме [math]O(n)[/math].
[math]\triangleleft[/math]
Теорема:
Максимальная глубина PST [math]O(\log\,n)[/math].
Доказательство:
[math]\triangleright[/math]
При рекурсивном вызове количество точек уменьшается как минимум в два раза.
[math]\triangleleft[/math]
Теорема:
Построение PST работает за [math]O(n\log\,n)[/math] времени.
Доказательство:
[math]\triangleright[/math]
Необходимо отсортировать точки по [math]y[/math] — для этого необходимо [math]O(n\log\,n)[/math]. Построение каждого дерева требует [math]O(k)[/math], где [math]k[/math] — количество точек в дереве, чтобы найти минимальную по [math]x[/math] точку, а также для построения новых списков точек для сыновей. На каждом уровне [math]O(n)[/math] точек, уровней [math]O(\log\,n)[/math], значит, в сумме тоже [math]O(n\log\,n)[/math].
[math]\triangleleft[/math]

Как отвечать на запрос?

Если [math]x[/math] корня больше [math]x_q[/math], то никакие точки дерева не могут быть в ответе. Будем хранить также минимальную и максимальную по [math]y[/math] точки, которые лежат в поддереве. Тогда, если отрезок запроса по [math]y[/math] не пересекается с отрезком по [math]y[/math] всех точек поддерева, то также никакие точки из дерева не должны быть добавлены в ответ. Если корень дерева лежит в нужном интервале, добавим его в ответ. Вызовем рекурсивно поиск ответа для двух сыновей.

Теорема:
Будет получен правильный ответ на запрос.
Доказательство:
[math]\triangleright[/math]
Вроде бы очевидно.
[math]\triangleleft[/math]
Теорема:
Ответ на запрос работает за [math]O(\log\,n+k)[/math], где [math]k[/math] — размер ответа.
Доказательство:
[math]\triangleright[/math]
Тут все сложнее. Представим, что мы ищем [math]y_1[/math] и [math]y_2[/math] (из запроса) в PST как в обычном дереве поиска по [math]y[/math]. Рассмотрим все точки, которые лежат между двумя путями поиска. Среди них рассмотрим точки с [math]x \lt = x_q[/math]. Ровно эти точки и есть ответ. Разобьем все вершины на три группы. Первая — вершины, которые лежат хотя бы на одном из двух путей. Их [math]O(\log\,n)[/math]. Вторая — вершины, которые находятся между двумя путями. Третья — все остальные. Время обработки вершин первой группы не превышает [math]O(\log\,n)[/math]. Как только мы зашли в вершину третей группы, сразу поймем, что отрезок по [math]y[/math] не пересекается с запросом и выйдем. А зайти в них мы можем только из вершин, которые лежат на двух путях (а их [math]O(\log\,n)[/math]). Отлично! Зайдя в вершину второй группы, мы либо ее добавим в ответ и продолжим, либо выйдем, так как единственная причина, почему корень не подошел — его [math]x[/math] слишком большой, а, значит, и все вершины поддерева не подходят. В итоге их обработка занимает [math]O(k)[/math]. В сумме все хорошо!
[math]\triangleleft[/math]

Немного псевдокода

 struct PST 
   point root
   point min_y, max_y
   PST *down, *up
 // pts sorted by y
 PST * build_PST(vector<point> pts)
   if (pts.size() == 0)
     return NULL
   min_y = max_ y = root = min_element(pts) // by x
   remove_element(pts, root)
   int mid = pts.size() / 2
   vector<point> down_pts = pts[0..mid]
   vector<point> up_pts = pts[mid+1..pts.size() - 1]
   down = build_PST(down_pts)
   up = build_PST(up_pts)
   if (down != NULL)
     min_y = min(min_y, down->min_y)
   if (up != NULL)
     max_y = max(max_y, up->max_y)
 void req(PST * tree, int y1, int y2, int x_max, vector<point> & ans)
   if (tree == NULL)
     return
   if (x_max < tree->root.x)
     return
   if (y1 > tree->max_y || y2 < tree->min_y)
     return
   if (tree->root in [y1..y2]x[-inf;x_max])
     answer.push_back(tree->root)
   req(tree->down, y1, y2, x_max, ans)
   req(tree->up, y1, y2, x_max, ans)