Ортогональный поиск — различия между версиями
Glukos (обсуждение | вклад) (→Сбалансированное дерево поиска) |
(→Сбалансированное дерево поиска) |
||
Строка 28: | Строка 28: | ||
Для этого возьмем любое сбалансированное дерево поиска и наполним его точками <tex>(x, y)</tex> из множества. В качестве ключа будет использоваться <tex>x</tex>-координата точки. Теперь модернизируем дерево: в каждой вершине дерева будем хранить отсортированный по <tex>y</tex>-координате массив точек, которые содержатся в соответствующем поддереве. <br>Рассмотрим на примере:<br> | Для этого возьмем любое сбалансированное дерево поиска и наполним его точками <tex>(x, y)</tex> из множества. В качестве ключа будет использоваться <tex>x</tex>-координата точки. Теперь модернизируем дерево: в каждой вершине дерева будем хранить отсортированный по <tex>y</tex>-координате массив точек, которые содержатся в соответствующем поддереве. <br>Рассмотрим на примере:<br> | ||
[[Файл:ortog_search_tree.png|600px]]<br> | [[Файл:ortog_search_tree.png|600px]]<br> | ||
− | + | Рассмотрим, как в такой структуре данных будет выглядеть поиск множества точек, находящихся в заданном прямоугольнике <tex>(x_{min}, x_{max}) \times (y_{min}, y_{max})</tex>. Для начала, найдем в дереве те точки, <tex>x</tex>-координата которых лежит в интервале <tex>(x_{min}, x_{max})</tex>. Сделаем это следующим образом: | |
− | + | # Найдем в дереве поиска вершины с минимальной и максимальной <tex>x</tex>-координатой из прямоугольника запроса, добавим их в искомое множество, обозначим их как <tex>v_l</tex> и <tex>v_r</tex>. | |
+ | # Добавим в искомое множество их наименьшего общего предка <tex>v_n</tex>. | ||
+ | # Для каждой из промежуточных вершин <tex>v_i</tex> на пути <tex>v_l \to v_n</tex> зафиксируем, из какого ребенка мы поднялись в вершину <tex>v_i</tex>. Если мы поднялись из левого сына, то добавим в искомое множество саму вершину <tex>v_i</tex>, а также множество точек, привязанных к правому сыну вершины <tex>v_i</tex>. Если же мы поднялись из правого сына, то не добавляем ничего. | ||
+ | # Повторим процесс для пути <tex>v_r \to v_n</tex>. Здесь ориентация сторон инвертирована: будем пополнять множество в том случае, если мы поднялись из правого сына. | ||
# Для каждого из полученных поддеревьев обратимся к массиву содержащихся в нем точек и запустим от него приведенную выше функцию <tex>range{\_}search(y_{min}, y_{max})</tex>. Все полученные таким образом точки и будут составлять ответ. | # Для каждого из полученных поддеревьев обратимся к массиву содержащихся в нем точек и запустим от него приведенную выше функцию <tex>range{\_}search(y_{min}, y_{max})</tex>. Все полученные таким образом точки и будут составлять ответ. | ||
{{TODO| t=запилить красивую и понятную картинку}} | {{TODO| t=запилить красивую и понятную картинку}} |
Версия 00:34, 5 июня 2012
Содержание
Простейший случай
Пусть дана прямая с точками на ней и отрезок. Точки даны в отсортированном порядке. Необходимо указать, какие из изначальных точек лежат на этом отрезке.
Данная задача решается с помощью функций из STL - upper_bound и lower_bound.
lower_bound возвращает итератор на первый элемент, больший либо равный данного.
upper_bound возвращает итератор на первый элемент множества со значением, большим данного.
Рассмотрим на примере:
Код реализации:
template<class RauIter, class OutIter, class Scalar> OutIter range_search(RauIter p, RauIter q, OutIter out) { return std::copy(lower_bound(p, q, l), upper_bound(p, q, r), out); }
Сбалансированное дерево поиска
Переходим к двумерному случаю. Пусть дано некоторое множество точек на плоскости. Нам необходимо ответить, какие именно из них лежат в некотором заданном прямоугольнике.
Для этого возьмем любое сбалансированное дерево поиска и наполним его точками
Рассмотрим на примере:
Рассмотрим, как в такой структуре данных будет выглядеть поиск множества точек, находящихся в заданном прямоугольнике . Для начала, найдем в дереве те точки, -координата которых лежит в интервале . Сделаем это следующим образом:
- Найдем в дереве поиска вершины с минимальной и максимальной -координатой из прямоугольника запроса, добавим их в искомое множество, обозначим их как и .
- Добавим в искомое множество их наименьшего общего предка .
- Для каждой из промежуточных вершин на пути зафиксируем, из какого ребенка мы поднялись в вершину . Если мы поднялись из левого сына, то добавим в искомое множество саму вершину , а также множество точек, привязанных к правому сыну вершины . Если же мы поднялись из правого сына, то не добавляем ничего.
- Повторим процесс для пути . Здесь ориентация сторон инвертирована: будем пополнять множество в том случае, если мы поднялись из правого сына.
- Для каждого из полученных поддеревьев обратимся к массиву содержащихся в нем точек и запустим от него приведенную выше функцию . Все полученные таким образом точки и будут составлять ответ.
TODO: запилить красивую и понятную картинку
Каждая из функций
будет работать в худшем случае за , отсюда получаем итоговое время выполнения запроса . Что касается памяти, то в сбалансированном дереве поиска слоев, а каждый слой хранит массивы, содержащие в сумме ровно точек, соответственно вся структура в целом занимает памяти.Такую структуру данных можно при необходимости обобщить на случай большей размерности. Пусть у нас есть множество точек из
-мерного пространства, каждая из которых представляется как координатных чисел: . Тогда, строя дерево поиска по координате , в каждой вершине будем хранить другое дерево поиска с ключом , составленное из точек, лежащих в соответствующем поддереве. В дереве поиска, составленном по предпоследней координате , уже не будет необходимости хранить в каждой вершине целое дерево, поскольку при переходе на последнюю координату дальнейший поиск производиться не будет, поэтому в вершинах будем хранить массивы, так же, как и в двумерном случае. Оценим занимаемую память и время запроса: при добавлении следующей координаты асимптотика обеих величин умножается на . Отсюда, получаем оценку на время запроса и на занимаемую память.Такой же результат можно получить с помощью сжатого многомерного дерева отрезков.
Прошитые отсортированные массивы
Для ускорения запроса можно "прошить" дерево поиска, а именно: каждый элемент массива, сохраненного в какой-либо вершине, соединить с элементами массивов, сохраненных в вершинах-детях. Соединять будем по следующему принципу: элемент
массива-предка соединим с элементами и каждого массива-ребенка. Тогда для выполнения завершающей фазы поиска нам достаточно будет посчитать и только на массиве, привязанному к корню дерева. Для получения границ на других массивах можно будет просто спуститься по ссылкам от массива-предка за . Таким образом, поиск теперь будет выполняться за , где — размерность пространства.TODO: здесь тоже надо что-нибудь нарисовать