Дерево интервалов

редактировать

В информатике дерево интервалов представляет собой древовидную структуру данных для удержания интервалов. В частности, он позволяет эффективно находить все интервалы, которые перекрываются с любым заданным интервалом или точкой. Он часто используется для оконных запросов, например, чтобы найти все дороги на компьютерной карте внутри прямоугольного окна просмотра или найти все видимые элементы внутри трехмерной сцены. Похожая структура данных - дерево сегментов .

Тривиальное решение - посетить каждый интервал и проверить, пересекает ли он заданную точку или интервал, для чего требуется O (n) {\ displaystyle O (n)}O (n) время, где n {\ displaystyle n}n - количество интервалов в коллекции. Поскольку запрос может возвращать все интервалы, например, если запрос представляет собой большой интервал, пересекающий все интервалы в коллекции, это асимптотически оптимально ; однако мы можем добиться большего, если рассмотрим алгоритмы, чувствительные к выводу, где время выполнения выражается в терминах m {\ displaystyle m}m , количества интервалов, создаваемых запрос. Деревья интервалов имеют время запроса O (log ⁡ n + m) {\ displaystyle O (\ log n + m)}{\ displaystyle O (\ log n + m)} и начальное время создания O (n log ⁡ n) {\ displaystyle O (n \ log n)}O (n \ log n) с ограничением потребления памяти O (n) {\ displaystyle O (n)}O (n) . После создания деревья интервалов могут быть динамическими, что позволяет эффективно вставлять и удалять интервалы за O (log ⁡ n) {\ displaystyle O (\ log n)}O (\ log n) времени. Если конечные точки интервалов находятся в пределах небольшого целого диапазона (например, в диапазоне [1,…, O (n)] {\ displaystyle [1, \ ldots, O (n)]}{\ displaystyle [1, \ ldots, O (n)]} ), существуют более быстрые и фактически оптимальные структуры данных со временем предварительной обработки O (n) {\ displaystyle O (n)}O (n) и временем запроса O (1 + m) {\ displaystyle O (1 + m)}{\ displaystyle O (1 + m)} для сообщения m {\ displaystyle m}m интервалов, содержащих заданную точку запроса (см. Очень простой вариант).

Содержание
  • 1 Наивный подход
  • 2 Центрированное дерево интервалов
    • 2.1 Построение
    • 2.2 Пересечение
      • 2.2.1 С точкой
      • 2.2.2 С интервалом
    • 2.3 Выше размеры
    • 2.4 Удаление
    • 2.5 Балансировка
  • 3 Расширенное дерево
    • 3.1 Запросы членства
    • 3.2 Пример Java: добавление нового интервала к дереву
    • 3.3 Пример Java: поиск точки или интервала в дереве
    • 3.4 Более высокие измерения
  • 4 Срединное или ориентированное на длину дерево
    • 4.1 Тест на перекрытие
    • 4.2 Добавление интервала
    • 4.3 Поиск всех перекрывающихся интервалов
  • 5 Ссылки
  • 6 Внешние ссылки
Наивный подход

В простом случае интервалы не перекрываются, и их можно вставить в простое двоичное дерево поиска и запросить в O (log ⁡ п) {\ displaystyle O (\ log n)}O (\ log n) время. Однако с произвольно перекрывающимися интервалами невозможно сравнить два интервала для вставки в дерево, поскольку порядок сортировки по начальным или конечным точкам может быть различным. Наивный подход может заключаться в построении двух параллельных деревьев, одно из которых упорядочено по начальной точке, а второе - по конечной точке каждого интервала. Это позволяет отбрасывать половину каждого дерева за O (log ⁡ n) {\ displaystyle O (\ log n)}O (\ log n) времени, но результаты должны быть объединены, что требует O (n) {\ displaystyle O (n)}O (n) время. Это дает нам запросы в O (n + log ⁡ n) = O (n) {\ displaystyle O (n + \ log n) = O (n)}{\ displaystyle O (n + \ log n) = O (n)} , что не лучше, чем грубый -сила.

Деревья интервалов решают эту проблему. В этой статье описываются два альтернативных дизайна для дерева интервалов, получившие название центрированного дерева интервалов и расширенного дерева.

Центрированное дерево интервалов

Запросы требуют O (log ⁡ n + m) {\ displaystyle O (\ log n + m)}{\ displaystyle O (\ log n + m)} времени, с n {\ displaystyle n}n - общее количество интервалов, а m {\ displaystyle m}m - количество полученных результатов. Конструкция требует O (n log ⁡ n) {\ displaystyle O (n \ log n)}O (n \ log n) времени, а для хранения требуется O (n) {\ displaystyle O (n)}O (n) пробел.

Построение

Учитывая набор n {\ displaystyle n}n интервалов в числовой строке, мы хотим построить структуру данных, чтобы мы могли эффективно получить все интервалы, перекрывающие другой интервал или точку.

Мы начинаем с того, что берем весь диапазон всех интервалов и делим его пополам в x center {\ displaystyle x _ {\ textrm {center}}}{\ displaystyle x _ {\ textrm {center}}} (на практике x center {\ displaystyle x _ {\ textrm {center}}}{\ displaystyle x _ {\ textrm {center}}} следует выбрать, чтобы дерево оставалось относительно сбалансированным). Это дает три набора интервалов, полностью слева от x center {\ displaystyle x _ {\ textrm {center}}}{\ displaystyle x _ {\ textrm {center}}} , которые мы назовем S left {\ displaystyle S_ {\ textrm {left}}}{\ displaystyle S _ {\ textrm {left}}} , полностью справа от x center {\ displaystyle x _ {\ textrm {center}}}{\ displaystyle x _ {\ textrm {center}}} , которые мы назовем S right {\ displaystyle S _ {\ textrm {right}}}{\ displaystyle S _ {\ textrm {right}}} , и те, которые перекрывают x center {\ displaystyle x _ {\ textrm {center}}}{\ displaystyle x _ {\ textrm {center}}} которые мы назовем S center {\ displaystyle S _ {\ textrm {center}}}{\ displaystyle S _ {\ textrm {center}}} .

Интервалы в S left {\ displaystyle S _ {\ textrm {left}}}{\ displaystyle S _ {\ textrm {left}}} и S right {\ displaystyle S _ {\ textrm {right}}}{\ displaystyle S _ {\ textrm {right}}} рекурсивно делятся таким же образом, пока не останется никаких интервалов.

Интервалы в S center {\ displaystyle S _ {\ textrm {center}}}{\ displaystyle S _ {\ textrm {center}}} , которые перекрывают центральную точку, хранятся в отдельной структуре данных, связанной с узлом в интервальное дерево. Эта структура данных состоит из двух списков, один из которых содержит все интервалы, отсортированные по их начальным точкам, а другой - все интервалы, отсортированные по их конечным точкам.

Результатом является двоичное дерево, в котором каждый узел хранит:

  • Центральная точка
  • Указатель на другой узел, содержащий все интервалы полностью слева от центра. точка
  • Указатель на другой узел, содержащий все интервалы полностью правее центральной точки
  • Все интервалы, перекрывающие центральную точку, отсортированные по их начальной точке
  • Все интервалы, перекрывающие центральная точка, отсортированная по конечной точке

Пересечение

Учитывая структуру данных, построенную выше, мы получаем запросы, состоящие из диапазонов или точек, и возвращаем все диапазоны в исходном наборе, перекрывающие этот ввод.

С точкой

Задача состоит в том, чтобы найти все интервалы в дереве, которые перекрывают заданную точку x {\ displaystyle x}x . Обход дерева выполняется с помощью аналогичного рекурсивного алгоритма, который использовался бы для обхода традиционного двоичного дерева, но с дополнительной логикой для поддержки поиска интервалов, перекрывающих «центральную» точку в каждом узле.

Для каждого узла дерева x {\ displaystyle x}x сравнивается с x center {\ displaystyle x _ {\ textrm {center}}}{\ displaystyle x _ {\ textrm {center}}} , средняя точка, используемая при построении узла выше. Если x {\ displaystyle x}x меньше x center {\ displaystyle x _ {\ textrm {center}}}{\ displaystyle x _ {\ textrm {center}}} , крайний левый набор интервалов, S left {\ displaystyle S _ {\ textrm {left}}}{\ displaystyle S _ {\ textrm {left}}} , считается. Если x {\ displaystyle x}x больше, чем x center {\ displaystyle x _ {\ textrm {center}}}{\ displaystyle x _ {\ textrm {center}}} , крайний правый набор интервалов, S right {\ displaystyle S _ {\ textrm {right}}}{\ displaystyle S _ {\ textrm {right}}} , считается.

Все интервалы в S center {\ displaystyle S _ {\ textrm {center}}}{\ displaystyle S _ {\ textrm {center}}} , которые начинаются до x {\ displaystyle x}x , должны перекрываться x {\ displaystyle x}x , если x {\ displaystyle x}x меньше x center {\ displaystyle x _ {\ textrm {center}}}{\ displaystyle x _ {\ textrm {center}}} .. Аналогичным образом, тот же метод применяется при проверке заданного интервала. Если заданный интервал заканчивается в y, а y меньше x center {\ displaystyle x _ {\ textrm {center}}}{\ displaystyle x _ {\ textrm {center}}} , все интервалы в S center {\ displaystyle S _ {\ textrm {center}}}{\ displaystyle S _ {\ textrm {center}}} , которые начинаются до y, также должны перекрывать заданный интервал.

Поскольку каждый узел обрабатывается, когда мы перемещаемся по дереву от корня к листу, диапазоны в его S center {\ displaystyle S _ {\ textrm {center}}}{\ displaystyle S _ {\ textrm {center}}} обрабатываются. Если x {\ displaystyle x}x меньше x center {\ displaystyle x _ {\ textrm {center}}}{\ displaystyle x _ {\ textrm {center}}} , мы знаем, что все интервалы в S center {\ displaystyle S _ {\ textrm {center}}}{\ displaystyle S _ {\ textrm {center}}} конец после x {\ displaystyle x}x , иначе они не могли перекрываться x center {\ displaystyle x _ {\ textrm {center}}}{\ displaystyle x _ {\ textrm {center}}} . Следовательно, нам нужно найти только те интервалы в S center {\ displaystyle S _ {\ textrm {center}}}{\ displaystyle S _ {\ textrm {center}}} , которые начинаются перед x {\ displaystyle x}x . Мы можем проконсультироваться со списками S center {\ displaystyle S _ {\ textrm {center}}}{\ displaystyle S _ {\ textrm {center}}} , которые уже созданы. Поскольку в этом сценарии нас интересуют только начала интервала, мы можем просмотреть список, отсортированный по началу. Предположим, мы нашли в этом списке ближайшее число, не превышающее x {\ displaystyle x}x . Все диапазоны от начала списка до найденной точки перекрываются x {\ displaystyle x}x , потому что они начинаются до x {\ displaystyle x}x и заканчиваются после x {\ displaystyle x}x (как мы знаем, потому что они перекрывают x center {\ displaystyle x _ {\ textrm {center}}}{\ displaystyle x _ {\ textrm {center}}} , который больше, чем х {\ displaystyle x}x ). Таким образом, мы можем просто начать перечисление интервалов в списке до тех пор, пока значение начальной точки не превысит x {\ displaystyle x}x .

Аналогично, если x {\ displaystyle x}x больше, чем x center {\ displaystyle x _ {\ textrm {center}}}{\ displaystyle x _ {\ textrm {center}}} , мы знаем, что все интервалы в S center {\ displaystyle S _ {\ textrm {center}}}{\ displaystyle S _ {\ textrm {center}}} должен начинаться перед x {\ displaystyle x}x , поэтому мы находим те интервалы, которые заканчиваются после x {\ displaystyle x}x , используя список, отсортированный по интервалу концовки.

Если x {\ displaystyle x}x точно соответствует x center {\ displaystyle x _ {\ textrm {center}}}{\ displaystyle x _ {\ textrm {center}}} , все интервалы в S center {\ displaystyle S _ {\ textrm {center}}}{\ displaystyle S _ {\ textrm {center}}} можно добавить к результатам без дальнейшей обработки, а обход дерева можно остановить.

С интервалом

Для интервала результатов r {\ displaystyle r}r , чтобы пересечь интервал нашего запроса q {\ displaystyle q}q должно выполняться одно из следующих условий:

  • начальная и / или конечная точка r {\ displaystyle r}r находится в q {\ displaystyle q}q ; или
  • r {\ displaystyle r}r полностью охватывает q {\ displaystyle q}q .

Сначала мы находим все интервалы с начальной и / или конечной точками внутри q {\ displaystyle q}q с использованием отдельно построенного дерева. В одномерном случае мы можем использовать дерево поиска, содержащее все начальные и конечные точки в наборе интервалов, каждая из которых имеет указатель на соответствующий интервал. Бинарный поиск в O (log ⁡ n) {\ displaystyle O (\ log n)}O (\ log n) времени для начала и конца q {\ displaystyle q}q показывает минимальные и максимальные точки для рассмотрения. Каждая точка в этом диапазоне ссылается на интервал, который перекрывает q {\ displaystyle q}q и добавляется в список результатов. Следует проявлять осторожность, чтобы избежать дублирования, поскольку интервал может как начинаться, так и заканчиваться в пределах q {\ displaystyle q}q . Это можно сделать с помощью двоичного флага на каждом интервале, чтобы отметить, был ли он добавлен в набор результатов.

Наконец, мы должны найти интервалы, которые заключают q {\ displaystyle q}q . Чтобы найти их, мы выбираем любую точку внутри q {\ displaystyle q}q и используем описанный выше алгоритм, чтобы найти все интервалы, пересекающие эту точку (опять же, стараясь удалить дубликаты).

Более высокие измерения

Структура данных интервального дерева может быть обобщена до более высокого измерения N {\ displaystyle N}N с идентичным временем запроса и построения и О (n журнал ⁡ n) {\ displaystyle O (n \ log n)}O (n \ log n) пробел.

Сначала создается дерево диапазонов в измерениях N {\ displaystyle N}N , которое позволяет эффективно извлекать все интервалы с начальной и конечной точками внутри область запроса R {\ displaystyle R}R . После того, как соответствующие диапазоны найдены, остается только те диапазоны, которые охватывают область в некотором измерении. Чтобы найти эти перекрытия, создаются деревья интервалов N {\ displaystyle N}N , и для каждого из них запрашивается одна ось, пересекающая R {\ displaystyle R}R . Например, в двух измерениях нижняя часть квадрата R {\ displaystyle R}R (или любой другой горизонтальной линии, пересекающей R {\ displaystyle R}R ) будет запрашиваться в дереве интервалов, построенном для горизонтальной оси. Точно так же левая сторона (или любая другая вертикальная линия, пересекающая R {\ displaystyle R}R ) будет опрошена по дереву интервалов, построенному на вертикальной оси.

Каждое дерево интервалов также нуждается в добавлении для более высоких измерений. В каждом узле, который мы просматриваем в дереве, x {\ displaystyle x}x сравнивается с S center {\ displaystyle S _ {\ textrm {center}}}{\ displaystyle S _ {\ textrm {center}}} найти совпадения. Вместо двух отсортированных списков точек, которые использовались в одномерном случае, строится дерево диапазонов. Это позволяет эффективно извлекать все точки в S center {\ displaystyle S _ {\ textrm {center}}}{\ displaystyle S _ {\ textrm {center}}} , которая перекрывает область R {\ displaystyle R}R .

Удаление

Если после удаления интервала из дерева узел, содержащий этот интервал, больше не содержит интервалов, этот узел может быть удален из дерева. Это сложнее, чем обычная операция удаления двоичного дерева.

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

Обычные операции удаления в двоичном дереве (для случая, когда удаляемый узел имеет двух дочерних узлов) включают продвижение узла дальше от листа до позиции удаляемого узла (обычно крайний левый дочерний элемент справа поддерево или крайний правый дочерний элемент левого поддерева).

Удаление узла с двумя дочерними элементами из двоичного дерева поиска с использованием упорядоченного предшественника (крайний правый узел в левом поддереве, помеченный 6).

В результате этого продвижения некоторые узлы, которые находились над повышенным узлом, станут его потомков; необходимо найти в этих узлах интервалы, которые также перекрывают продвинутый узел, и переместить эти интервалы в продвинутый узел. Как следствие, это может привести к появлению новых пустых узлов, которые должны быть удалены снова по тому же алгоритму.

Балансировка

Те же проблемы, которые влияют на удаление, также влияют на операции вращения; вращение должно сохранять инвариант, что узлы хранятся как можно ближе к корню.

Дополненное дерево
Расширенное дерево с низким значением в качестве ключа и максимальным высоким в качестве дополнительной аннотации. Например, при проверке, перекрывает ли данный интервал [40, 60) интервалы в дереве, показанном выше, мы видим что он не перекрывает интервал [20, 36) в корне, но поскольку t Если низкое значение корня (20) меньше искомого высокого значения (60), мы должны искать правое поддерево. Максимальное значение 41 для левого поддерева превышает искомое минимальное значение (40), поэтому мы также должны искать в левом поддереве. Однако оба потомка узла [3, 41) имеют максимальные максимумы меньше 40, поэтому поиск левого поддерева на этом заканчивается, и нет необходимости искать их.

Другой способ представления интервалов описан в Cormen и другие. (2009, Раздел 14.3: Деревья интервалов, стр. 348–354).

И вставка, и удаление требуют O (log ⁡ n) {\ displaystyle O (\ log n)}O (\ log n) времени, с n {\ displaystyle n}n - общее количество интервалов в дереве до операции вставки или удаления.

Расширенное дерево может быть построено из простого упорядоченного дерева, например, двоичного дерева поиска или самобалансирующегося двоичного дерева поиска, упорядоченного по 'младшему' значения интервалов. Затем к каждому узлу добавляется дополнительная аннотация, в которой записывается максимальное верхнее значение среди всех интервалов, начиная с этого узла. Поддержание этого атрибута включает обновление всех предков узла снизу вверх всякий раз, когда узел добавляется или удаляется. Это занимает всего O (h) шагов на добавление или удаление узла, где h - высота узла, добавленного или удаленного в дереве. Если есть какие-либо вращения дерева во время вставки и удаления, затронутые узлы также могут нуждаться в обновлении.

Теперь известно, что два интервала A {\ displaystyle A}A и B {\ displaystyle B}B перекрываются только тогда, когда оба A низкий ≤ B высокий {\ displaystyle A _ {\ textrm {low}} \ leq B _ {\ textrm {high}}}{\ displaystyle A _ {\ textrm {low}} \ leq B _ {\ textrm {high}}} и A высокий ≥ B низкий {\ displaystyle A _ {\ textrm {high}} \ geq B _ {\ textrm {low}}}{\ displaystyle A _ {\ textrm {high}} \ geq B _ {\ textrm {low}}} . При поиске в деревьях узлов, перекрывающихся с заданным интервалом, вы можете сразу же пропустить:

  • все узлы справа от узлов, нижнее значение которых превышает конец заданного интервала.
  • все узлы, у которых есть свои максимальное максимальное значение ниже начала данного интервала.

Запросы членства

Некоторая производительность может быть получена, если дерево избегает ненужных обходов. Это может происходить при добавлении уже существующих интервалов или удалении несуществующих интервалов.

Общий порядок интервалов можно определить, упорядочив их сначала по нижним границам, а затем по верхним границам. Затем проверка принадлежности может быть выполнена за O (log ⁡ n) {\ displaystyle O (\ log n)}O (\ log n) времени по сравнению с O (k + log ⁡ n) { \ displaystyle O (k + \ log n)}{\ Displaystyle О (к + \ журнал п)} время, необходимое для поиска дубликатов, если k {\ displaystyle k}k интервалы перекрывают интервал, который нужно вставить или удалить. Это решение имеет то преимущество, что не требует дополнительных конструкций. Изменение строго алгоритмическое. Недостатком является то, что запросы о членстве занимают O (log ⁡ n) {\ displaystyle O (\ log n)}O (\ log n) времени.

В качестве альтернативы, со скоростью O (n) {\ displaystyle O (n)}O (n) памяти, запросы членства в ожидаемое постоянное время могут быть реализованы с помощью хэш-таблицы, обновленной в ногу с интервальным деревом. Это не обязательно может удвоить общую потребность в памяти, если интервалы хранятся по ссылке, а не по значению.

Пример Java: добавление нового интервала к дереву

Ключом каждого узла является сам интервал, поэтому узлы упорядочиваются сначала по низкому значению и, наконец, по большому значению, а значение каждый узел является конечной точкой интервала:

public void add (Interval i) {put (i, i.getEnd ()); }

Пример Java: поиск точки или интервала в дереве

Для поиска интервала необходимо пройти по дереву, используя ключ (n.getKey ()) и высокое значение (n.getValue ()) для исключения ветвей, которые не могут перекрывать запрос. Самый простой случай - это точечный запрос:

// Поиск всех интервалов, содержащих «p», начиная с // узла «n» и добавляя соответствующие интервалы в список «result» public void search (IntervalNode n, Point p, List result) {// Не искать узлы, которые не существуют if (n == null) return; // Если p находится справа от крайней правой точки любого интервала // в этом узле и во всех дочерних узлах, совпадений не будет. если (p.compareTo (n.getValue ())>0) return; // Поиск левых дочерних элементов search (n.getLeft (), p, result); // Проверяем этот узел if (n.getKey (). Contains (p)) result.add (n.getKey ()); // Если p находится слева от начала этого интервала, // тогда он не может быть ни в одном дочернем элементе справа. if (p.compareTo (n.getKey (). getStart ()) < 0) return; // Otherwise, search right children search(n.getRight(), p, result); }

где

a.compareTo (b)возвращает отрицательное значение, если < b
a.compareTo (b)возвращает ноль, если a = b
a.compareTo (b)возвращает положительное значение, если a>b

Код для поиска интервала аналогичен, за исключением проверки посередине:

// Проверяем этот узел, если (n.getKey (). OverlapsWith (i)) result.add (n.getKey ());

overlapsWith ()определяется как:

public boolean overlapsWith (Interval other) {return start.compareTo (other.getEnd ()) <= 0 end.compareTo(other.getStart())>= 0;}

Более высокие измерения

Расширенные деревья можно расширить до более высоких измерений, циклически перебирая измерения на каждом уровне дерево. Например, для двух измерений нечетные уровни дерева могут содержать диапазоны для координаты x, а четные уровни содержат диапазоны для координаты Y. Этот подход эффективно преобразует структуру данных из расширенного двоичного дерева в расширено kd-tree, что значительно усложняет алгоритмы балансировки для i вставки и удаления.

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

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

Поначалу дополнительные затраты на вложенные деревья могут показаться непомерно высокими, но обычно это не так. Как и в случае с ранее не вложенным решением, для каждой координаты x требуется один узел, что дает одинаковое количество узлов для обоих решений. Единственные дополнительные накладные расходы - это вложенные древовидные структуры, по одной на вертикальный интервал. Эта структура обычно имеет незначительный размер и состоит только из указателя на корневой узел и, возможно, количества узлов и глубины дерева.

Медиальное или ориентированное на длину дерево

Медиальное или ориентированное на длину дерево похоже на расширенное дерево, но симметрично, с двоичным деревом поиска, упорядоченным по средним точкам интервалов. В каждом узле имеется ориентированная на максимум двоичная куча, упорядоченная по длине интервала (или половине длины). Также мы храним минимальное и максимальное возможное значение поддерева в каждом узле (таким образом, симметрия).

Тест на перекрытие

Использование только начального и конечного значений двух интервалов (ai, bi) {\ displaystyle \ left (a_ {i}, b_ {i} \ right)}\ left (a _ {{i}}, b_ {i} \ right) , для i = 0, 1 {\ displaystyle i = 0,1}i = 0,1 тест на перекрытие можно выполнить следующим образом:

a 0 < b 1 {\displaystyle a_{0}{\ displaystyle a_ {0} <b_ {1}} и a 1>b 0 {\ displaystyle a_ {1}>b_ {0}}{\displaystyle a_{1}>b_ {0}}

Это можно упростить, используя сумму и разницу:

si = ai + bi {\ displaystyle s_ {i} = a_ {i} + b_ {i}}{\ displaystyle s_ {i} = a_ {i} + b_ {i}}

di = bi - ai {\ displaystyle d_ {i} = b_ {i} -a_ {i}}{\ displaystyle d_ {i} = b_ {i} -a_ {i}}

Что сокращает тест на перекрытие до :

| s 1 - s 0 | < d 0 + d 1 {\displaystyle \left|s_{1}-s_{0}\right|{\ displaystyle \ left | s_ { 1} -s_ {0} \ right | <d_ {0} + d_ {1}}

Добавление интервала

Добавление новых интервалов в дерево такое же, как для двоичного дерева поиска с использованием среднего значения в качестве ключа. Мы нажимаем di {\ displaystyle d_ {i}}d_ { i} в двоичную кучу, связанную с узлом, и обновить минимальное и максимальное возможные значения, связанные с все высшие узлы.

Поиск всех перекрывающихся интервалов

Давайте использовать aq, bq, mq, dq {\ displaystyle a_ {q}, b_ {q}, m_ {q}, d_ {q} }}a_ {q }, b_ {q}, m_ {q}, d_ {q} для интервала запроса и M n {\ displaystyle M_ {n}}M_ {n} для ключа узла (по сравнению с mi {\ displaystyle m_ {i}}m_ {i} интервалов)

Начиная с корневого узла, в каждом узле сначала мы проверяем, возможно ли, что наш интервал запроса перекрывается с поддеревом узла, используя минимальные и максимальные значения узел (если это не так, мы не продолжим для этого узла).

Затем мы вычисляем min {di} {\ displaystyle \ min \ left \ {d_ {i} \ right \}}\ min \ left \ {d_ {i} \ right \} для интервалов внутри этого узла (не его дочерних элементов) для перекрытия с интервалом запроса (зная mi = M n {\ displaystyle m_ {i} = M_ {n}}m_ {i} = M_ {n} ):

min {di} = | m q - M n | - dq {\ displaystyle \ min \ left \ {d_ {i} \ right \} = \ left | m_ {q} -M_ {n} \ right | -d_ {q}}\ min \ left \ {d_ {i} \ right \} = \ left | m_ {q} -M_ {n} \ справа | -d_ {q}

и выполнить запрос на его двоичная куча для di {\ displaystyle d_ {i}}d_ { i} больше, чем min {di} {\ displaystyle \ min \ left \ {d_ {i} \ right \} }\ min \ left \ {d_ {i} \ right \}

Затем мы проходим через правые и левые потомки узла, делая то же самое.

В худшем случае мы должны сканировать все узлы двоичного дерева поиска, но, поскольку запрос двоичной кучи является оптимальным, это приемлемо (двумерная задача не может быть оптимальной в обоих измерениях)

Ожидается, что этот алгоритм будет быстрее, чем традиционное дерево интервалов (расширенное дерево) для операций поиска. На практике добавление элементов происходит немного медленнее, хотя порядок увеличения такой же.

Ссылки
  1. ^. Задачи с интервальным колющим ударом в малых целых диапазонах. ДОИ. ISAAC'09, 2009
  2. ^Диапазон запросов # Операторы полугруппы
Внешние ссылки
  • CGAL: Библиотека алгоритмов вычислительной геометрии на C ++ содержит надежную реализацию Range Trees
  • Boost.Icl предлагает реализации C ++ наборов интервалов и карт.
  • IntervalTree (Python) - центрированное дерево интервалов с балансировкой AVL, совместимое с тегированными интервалами
  • Interval Tree (C #) - расширенное дерево интервалов с AVL-балансировкой
  • Interval Tree (Ruby) - центрированное дерево интервалов, неизменяемое, совместимое с тегированными интервалами
  • IntervalTree (Java) - расширенное дерево интервалов, с балансировкой AVL, поддерживающее перекрытие, поиск, интерфейс коллекции, интервалы, связанные с идентификатором
Последняя правка сделана 2021-05-24 05:22:53
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте