Существует ли какая-либо структура данных, которая поддерживает набор множеств (конечного наземного множества), поддерживающий следующие операции? Любое сублинейное время работы будет оценено?
- Инициировать пустой набор.
- Добавить элемент в набор.
- Учитывая два набора, сообщают, пересекаются ли они.
data-structures
sets
Давэй Хуан
источник
источник
Ответы:
Если каждый набор поддерживает запись о том, какие другие наборы существуют, и у вас есть в общей сложности наборов, вы можете легко превратить любую структуру данных для коллекции ( например, двоичные деревья поиска и т. Д. ) В ту, где вы можете получить элемент пересечения двух множеств во времени O ( log s ) .с > 0 O ( журналс )
Каждый набор должен иметь уникальный идентификатор из некоторого полностью упорядоченного набора. Если вы явно дадите имена своим наборам тогда идентификатор может быть просто индексом.S1, S2, ...
Вы должны реализовать «реестр» наборов; структура данных, которая поддерживает коллекцию всех наборов, которые вы определили. Реестр должен быть реализован в виде структуры данных дерева поиска, чтобы обеспечить легкий поиск ( например, если вы хотите удалить набор) и обход наборов по линейному времени.
Каждый набор также поддерживает «индекс» каждого из других наборов - не их копию , а скорее структуру данных, которая индексируется метками других наборов. Этот индекс будет использоваться для поддержания, для каждого множества S к , бинарное дерево поиска всех элементов S J ∩ S K . (Два набора S j и S k совместно используют одну копию этого дерева поиска.)SJ SК SJ∩ SК SJ SК
инициализация
Инициализация набора состоит из O ( 1 ) операций по инициализации дерева его элементов, O ( s ) операций при инициализации (копировании из реестра) индекса для набора T и O ( s log s ) операции при обходе реестра, чтобы добавить T в индексы каждого из других наборов S j . В индексе T мы создаем деревья поиска, представляющие T ∩ S j = ∅T= ∅ O ( 1 ) O ( s ) T O ( s log)с ) T SJ T T∩ SJ= ∅ для других множеств ; мы копируем тот же указатель для индекса S j .SJ SJ
Добавление элемента в наборT
Добавление некоторого к множеству T обычно занимает время O ( log n T ) , где n T = | T | , Мы также проверяем членство x в каждом из других наборов S 1 , S 2 , … , что занимает время O ( log n S 1 + log n S 2 + ⋯ ) ⊆ O ( s log nx ∈ V T O ( журналNT) NT= | T| Икс S1, S2, ... где n = | V | это размер юниверса (или самого большого набора S j ), а s это количество наборов в реестре. Для каждого множества S J такоечто х ∈ S J ,также вставка х в индекс для множества S J ∩ T . Для каждого такого набора S j для поиска S j требуется O ( log s + log n T ) времени
Если вы не допускаете дубликаты в наборах, мы можем сэкономить время в том случае, когда уже за счет отказа от тестирования членства и вставок для других наборов Т . «Вставка» в случае, если x уже присутствует, занимает только время O ( log n T ) .x ∈ S T Икс O ( журналNT)
Тестирование пересечения
Индекс каждого набора поддерживается точно, чтобы позволить быструю оценку того, пересекаются ли два набора и S k . Для набора S j , просто проверив его индекс для набора S k , мы можем не только определить во времени O ( log s ) , пересекает ли S j S k , но мы также можем извлечь двоичное дерево, содержащее весь набор S j ∩ S k .SJ SК SJ SК O ( журналс ) SJ SК SJ∩ SК
Удаление элемента
Чтобы удалить элемент из множества T , мы удаляем его не только из дерева поиска для самого T , но и с каждого из пересечений S j ∩ T для множеств S j в его индексе. Это занимает время O ( s log n T ) , где n T = | T | ,Икс T T SJ∩ T SJ O ( s log)NT) NT= | T|
Установить удаление
Из-за затрат на поиск в реестре, если у вас много наборов, может быть желательно удалить наборы, когда они больше не нужны. Обходя весь реестр, мы можем удалить из индекса всех других наборов S j за время O ( s n T ) , в котором преобладает стоимость удаления дерева поиска, представляющего S j ∩ T, для каждого из других наборов S j где n T = | T | ,S SJ O ( с нT) SJ∩ T SJ NT= | T|
замечания
Если вы предполагаете только реализовать постоянное количество наборов, то приведенное выше время выполнения уменьшится до:
инициализация:O ( 1 )
вставка элемента:O ( журналн )
проверка пересечения (и поиск пересечения):O ( 1 )
удаление элемента:O ( журналNT)
установить удаление:O(nS)
где - размер наибольшего набора в реестре, а n T = | T | для набора T, над которым вы работаете.n nT=|T| T
Если вы ожидаете иметь наборы , где V - ваша вселенная, вам может потребоваться другая структура данных, если вы хотите, чтобы эти операции работали в сублинейном времени. Тем не менее, если у вас есть пары наборов, пересечение которых, как вы знаете, вы никогда не будете тестировать, вы можете уменьшить размер индекса для наборов (не включая наборы, пересечение которых вы будете тестировать) или использовать более одного реестра ( по одному на каждую коллекцию множеств, пересечение которых вы можете проверить). На самом деле, реестр полезен только в том случае, если вам нужен централизованный контроль за тем, чтобы каждая пара наборов имела записи друг друга в индексе: в некоторых случаях это может быть полезно при инициализации набора S ad hoc.O(|V|) V S просто записатькаждое новое множество входит в индексы других множеств чье пересечение с S вас интересует.T S
источник
Существуют структуры данных, которые позволяют вам делать это менее чем за линейное время, даже для входных данных в худшем случае. См. Http://research.microsoft.com/pubs/173795/vldb11intersection.pdf (и ссылки на документы там).
Если ваши два набора S и T имеют большое пересечение, и у вас есть словарь для S, поиск элементов T в случайном порядке должен быстро дать вам общий элемент. Наиболее сложный случай, когда размер пересечения равен 0 или 1.
источник
Обычно ваш язык программирования поддерживает структуру данных с уникальными элементами. В целом существует три популярных подхода: деревья, хэши и битовые маски. Элементы дерева должны быть сопоставимы, элементы Hash должны быть хешируемыми, а элементы Bitmask должны иметь какой-либо способ преобразования в целые числа.
Набор деревьев будет поддерживать вставку в O (log n) и тестирование пересечений в худшем случае O (n log n).
Хеш-набор будет поддерживать вставку в амортизированном O (1 * h), где 'h' - время выполнения алгоритма хеширования, и проверку пересечения в худшем случае O (n).
Наборы битовых масок обычно не используются, как наборы деревьев и хешей.
источник
Если ваш случай допускает ложные положительные ответы, я бы использовал Bloom Filter с одной хэш-функцией.
Вы можете реализовать это следующим образом:
Init пустой набор
Добавить элемент в набор.
Учитывая два набора (B1, B2), сообщите, пересекаются ли они.
сложность
источник