Дерево интервалов (interval tree) и пересечение точки с множеством интервалов — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Псевдокод)
Строка 1: Строка 1:
 +
{| class="wikitable" align="center" style="color: red; background-color: black; font-size: 56px; width: 800px;"
 +
|+
 +
|-align="center"
 +
|'''НЕТ ВОЙНЕ'''
 +
|-style="font-size: 16px;"
 +
|
 +
24 февраля 2022 года российское руководство во главе с Владимиром Путиным развязало агрессивную войну против Украины. В глазах всего мира это военное преступление совершено от лица всей страны, всех россиян.
 +
 +
Будучи гражданами Российской Федерации, мы против своей воли оказались ответственными за нарушение международного права, военное вторжение и массовую гибель людей. Чудовищность совершенного преступления не оставляет возможности промолчать или ограничиться пассивным несогласием.
 +
 +
Мы убеждены в абсолютной ценности человеческой жизни, в незыблемости прав и свобод личности. Режим Путина — угроза этим ценностям. Наша задача — обьединить все силы для сопротивления ей.
 +
 +
Эту войну начали не россияне, а обезумевший диктатор. И наш гражданский долг — сделать всё, чтобы её остановить.
 +
 +
''Антивоенный комитет России''
 +
|-style="font-size: 16px;"
 +
|Распространяйте правду о текущих событиях, оберегайте от пропаганды своих друзей и близких. Изменение общественного восприятия войны - ключ к её завершению.
 +
|-style="font-size: 16px;"
 +
|[https://meduza.io/ meduza.io], [https://www.youtube.com/c/popularpolitics/videos Популярная политика], [https://novayagazeta.ru/ Новая газета], [https://zona.media/ zona.media], [https://www.youtube.com/c/MackNack/videos Майкл Наки].
 +
|}
 +
 
[[Категория: Вычислительная геометрия]]
 
[[Категория: Вычислительная геометрия]]
 
== Определение ==
 
== Определение ==

Версия 08:29, 1 сентября 2022

НЕТ ВОЙНЕ

24 февраля 2022 года российское руководство во главе с Владимиром Путиным развязало агрессивную войну против Украины. В глазах всего мира это военное преступление совершено от лица всей страны, всех россиян.

Будучи гражданами Российской Федерации, мы против своей воли оказались ответственными за нарушение международного права, военное вторжение и массовую гибель людей. Чудовищность совершенного преступления не оставляет возможности промолчать или ограничиться пассивным несогласием.

Мы убеждены в абсолютной ценности человеческой жизни, в незыблемости прав и свобод личности. Режим Путина — угроза этим ценностям. Наша задача — обьединить все силы для сопротивления ей.

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

Антивоенный комитет России

Распространяйте правду о текущих событиях, оберегайте от пропаганды своих друзей и близких. Изменение общественного восприятия войны - ключ к её завершению.
meduza.io, Популярная политика, Новая газета, zona.media, Майкл Наки.

Определение

Дерево интервалов (interval tree) позволяет решать следующую задачу. Дано множество отрезков [math]I=\{[x_1, x'_1], \ldots, [x_n, x'_n]\}[/math] и множество запросов. Каждый запрос характеризуется точкой [math]q_x[/math]. Для каждого запроса необходимо определить множество отрезков из [math]I[/math], которые содержат в себе [math]q_x[/math]. Построение дерева интервалов занимает время [math]O(n \log\,n)[/math], а также [math]O(n)[/math] памяти. На каждый запрос дерево интервалов позволяет отвечать за [math]O(\log\,n + k)[/math], где [math]k[/math] — размер ответа на запрос.

Построение

Чтобы построить дерево интервалов сделаем следующее. Найдем медиану множества всех координат отрезков [math]x_{mid}[/math]. Это можно сделать за [math]O(n)[/math] (см. Поиск k-ой порядковой статистики). Далее разделим все отрезки на три группы: те, которые лежат строго слева (справа) от [math]x_{mid}[/math] и все остальные. Для первых двух групп рекурсивно построим дерево интервалов. Все остальные отрезки будем хранить в корне дерева. Создадим два списка отрезков, которые пересекают [math]x_{mid}[/math]. В одном отсортируем их по левой координате, в другом — по правой.

Теорема:
Дерево интервалов имеет глубину [math]O(\log\,n)[/math].
Доказательство:
[math]\triangleright[/math]
При каждом рекурсивном вызове построения дерева множество концов отрезков уменьшается более чем в два раза. Значит, после [math]O(\log\,n)[/math] рекурсивных вызовов, множество отрезков будет пусто.
[math]\triangleleft[/math]
Теорема:
Дерево интервалов занимает [math]O(n)[/math] памяти.
Доказательство:
[math]\triangleright[/math]
По построению каждый отрезок был добавлен ровно в два списка. Вершин в двоичном дереве глубины [math]O(\log\,n)[/math], очевидно, [math]O(n)[/math].
[math]\triangleleft[/math]
Теорема:
Построение дерева интервалов работает за [math]O(n\log\,n)[/math] времени.
Доказательство:
[math]\triangleright[/math]
Отдельно оценим время, которое необходимо для создания всех отсортированных списков отрезков. Т. к. их суммарная длина [math]O(n)[/math], требуется [math]O(n\log\,n)[/math] времени. Теперь рассмотрим время, необходимое для рекурсивных вызовов. Для каждой вершины дерева это время равно [math]O(m)[/math], где [math]m[/math] — количество отрезков, которые были переданы рекурсивно. Просуммируем это время по всем [math]O(\log\,n)[/math] "слоям" дерева. Под каждым слоем понимаются все вершины дерева, которые лежат на одной и той же глубине. Т. к. в каждом слое множества отрезков для любых двух вершин не пересекаются, то суммарное количество отрезков в слое равно [math]O(n)[/math]. Слоев всего [math]O(\log\,n)[/math], значит, имеем общую оценку [math]O(n\log\,n)[/math] для построения всего дерева.
[math]\triangleleft[/math]

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

В каждой вершине дерева хранится [math]x_{mid}[/math], ссылки на два поддерева, а также два отсортированных списка отрезков. В вершине хранятся только те отрезки, которые пересекают [math]x_{mid}[/math]. Необходимо рассмотреть два симметричных случая. Покажем, что делать в одном из них. Пусть, например, [math]q_x\gt x_{mid}[/math]. Тогда в ответ нужно добавить некоторый суффикс отрезков, которые отсортированы по правому концу. Запустимся рекурсивно от правого дерева (т. к. понятно, что никакой отрезок, который хранится в левом поддереве не может содержать [math]q_x[/math]). Аналогично разбирается случай [math]q_x \lt x_{mid}[/math].

Теорема:
Ответ на запрос происходит за [math]O(\log\,n + answer)[/math] времени.
Доказательство:
[math]\triangleright[/math]
Глубина дерева равна [math]O(\log\,n)[/math], значит может быть только [math]O(\log\,n)[/math] рекурсивных вызовов. В каждой вершине ответ происходит за [math]O(answer)[/math], т. к. может быть просмотрен только один отрезок, который не должен быть добавлен в ответ.
[math]\triangleleft[/math]

Псевдокод

 struct interval_tree
   interval_tree *left, *right
   int x_mid
   vector<segment> left_segments, right_segments;
 interval_tree build_tree(vector<segment> segments)
   if (segment.size() == 0)
     return NULL
   x_mid = mid_element(segments.all_coordinates)
   for (segment s : segments)
     if (s.right < x_mid)
       left_child.add(s);
     if (s.left > x_mid)
       right_child.add(s);
     if (s.left <= x_mid && s.right >= x_mid)
       left_segments.add(s);
       right_segments.add(s);
   sort(left_segments) // by increasing of x_mid - segment.left
   sort(right_segments) // by decreasing of segment.right - x_mid
   result.left = build(left_child);
   result.right = build(right_child);
   result.left_segments = left_segments;
   result.right_segments = right_segments;
   result.x_mid = x_mid;
   return result;
 vector<segment> get_ans(interval_tree tree, int q_x) 
   if (tree == NULL)
     return empty_result;
   if (q_x < tree.x_mid)
     result = get_ans(tree.left, q_x);
   if (q_x > tree.x_mid)
     result = get_ans(tree.right, q_x);
   if (q_x < tree.x_mid)
     it = left_segments.size() - 1;
     while (it != -1 && left_segment[it].left <= q_x)
       result.add(left_segments[it--]);
   if (q_x >= tree.x_mid)
     it = right_segments.size() - 1;
     while (it != -1 && right_segments[it].right >= q_x)
       result.add(right_segments[it--]);
   return result;