Jeśli każdy zestaw zachowuje zapis istniejących zestawów i masz w sumie s>0 zestawów, możesz łatwo przekształcić dowolną strukturę danych dla kolekcji ( np. Drzewa wyszukiwania binarnego itp. ) W jedną, w której możesz pobrać element przecięcia dwóch zbiorów w czasie O(logs) .
Każdy zestaw powinien mieć unikalny identyfikator z całkowicie uporządkowanego zestawu. Jeśli wyraźnie określisz swoje zestawy S1,S2,… identyfikatorem może być po prostu indeks.
Powinieneś wdrożyć „rejestr” zestawów; struktura danych, która utrzymuje kolekcję wszystkich zestawów, które zdefiniowałeś. Rejestr powinien zostać zaimplementowany jako struktura danych drzewa wyszukiwania, aby umożliwić łatwe wyszukiwanie ( np. Jeśli chcesz usunąć zestaw) i przechodzenie zestawów w czasie liniowym.
Każdy zestaw utrzymuje również „indeks” z każdym z pozostałych zestawów - nie kopiowanie z nich, ale raczej strukturę danych, która jest indeksowana przez etykietach innych zestawach. Wskaźnik ten zostanie wykorzystany do utrzymania, dla każdego zestawu S k , wyszukiwanie binarne drzewo wszystkich elementów S jSjSk . (Dwa zestawy S j i S k współużytkują jedną kopię tego drzewa wyszukiwania.)Sj∩SkSjSk
Inicjalizacja
Inicjalizacja zestawu składa się z O ( 1 ) operacje zainicjować drzewo jej elementów, O ( s ) operacje jak zainicjować (kopiowanie z rejestru) wskaźnik dla zbioru T i O ( s dziennika s ) podczas przechodzenia przez rejestr, aby dodać T do indeksów każdego z pozostałych zestawów S j . W indeksie T tworzymy drzewa wyszukiwania reprezentujące T ∩ S j = ∅T=∅O(1)O(s)TO(slogs)TSjTT∩Sj=∅Dla innych zestawów ; kopiujemy ten sam wskaźnik dla indeksu S j .SjSj
Dodanie elementu do zestawu T
Dodanie pewnej do zbioru T zajmuje jak zwykle czas O ( log n T ) , gdzie n T = | T | . Testujemy również członkostwo x w każdym innym zestawie S 1 , S 2 , … , co zajmuje czas O ( log n S 1 + log n S 2 + ⋯ ) ⊆ O ( s log nx∈VTO(lognT)nT=|T|xS1,S2,… gdzie n = | V |
O(lognS1+lognS2+⋯)⊆O(slogn),
n=|V|jest wielkość wszechświata (lub największy zbiór
) i
s oznacza liczbę zestawów w rejestrze. Dla każdego zestawu
S j takie, że
x ∈ S j także wkładka
X do indeksu do zbioru
S j ∩ T . Dla każdego zestawu takiego
S j ma to
O ( LOG s + log n T ) czasu, aby wyszukać
S jSjsSjx∈SjxSj∩TSjO(logs+lognT)Sj indeks
i wstaw
x w
S j ∩ T ; we wszystkich zestawach
S 1 , S 2 , …TxSj∩TS1,S2,… zajmuje to czas
. Jeśli założymy, że liczba zestawów
S j jest znacznie mniejszy niż rozmiar wszechświata
V, (to znaczy, jeżeli założymy,
Ś « n ) Całkowity czas wkładka jest wówczas
O ( y log n )O(slogs+slognT)SjVs≪nO(slogn).
Jeśli nie dopuszczać duplikaty w zestawach, możemy zaoszczędzić czas w przypadku, gdy już o rezygnacji test członkostwa i wstawki dla innych zestawów T . „Wstawienie” w przypadku, gdy x jest już obecny, zajmuje tylko czas O ( log n T ) .x∈STxO(lognT)
Testowanie skrzyżowań
Indeks każdego zestawu jest utrzymywana właśnie w celu umożliwienia szybkiej oceny, czy dwa zestawy i S k przecinać. Przez zestaw S j , po prostu przez sprawdzenie jego indeks dla zestaw S k , nie możemy określić tylko w czasie O ( log ów ) czy S J przecina S k , ale możemy także pobierać binarne drzewo zawierającą cały zestaw S j ∩ S k .SjSkSjSkO(logs)SjSkSj∩Sk
Usuwanie elementu
Aby usunąć element ze zbioru T , usuwamy go nie tylko z drzewa wyszukiwania samego T , ale z każdego przecięcia S j ∩ T dla zbiorów S j w jego indeksie. Wymaga to czasu O ( s log n T ) , gdziexTTSj∩TSjO(slognT).nT=|T|
Ustaw usuwanie
Ze względu na narzut związany z przeszukiwaniem rejestru, jeśli masz wiele zestawów, pożądane może być ich usunięcie, gdy nie będą już potrzebne. Przez przejeżdżające cały rejestr, możemy usuwania z wykaz wszystkich innych zestawów S j w czasie O ( y n T ) , zdominowany przez koszt usuwania drzewa wyszukiwania odpowiadający S j ∩ T dla każdego z innych zestawów S j , gdzie n T = | T | .SSjO(snT)Sj∩TSjnT=|T|
Uwagi
Jeśli spodziewasz się zaimplementować tylko stałą liczbę zestawów, powyższe czasy działania zmniejszają się do:
inicjalizacja: O(1)
wstawienie elementu: O(logn)
testowanie skrzyżowania (i odzyskanie skrzyżowania): O(1)
usunięcie elementu: O(lognT)
usunięcie zestawu: O(nS)
gdzie jest rozmiarem największego zestawu w rejestrze, a n T = | T | dla zestawu T, na którym pracujesz.nnT=|T|T
Jeśli spodziewasz się mieć zestawów , gdzie V jest twoim wszechświatem, możesz potrzebować innej struktury danych, jeśli chcesz, aby operacje te działały w czasie sublinearnym. Jeśli jednak masz pary zestawów, o których przecięciu wiesz, że nigdy nie będziesz testować, możesz być w stanie zmniejszyć rozmiar indeksu dla tych zestawów (nie włączając żadnych zestawów, których przecięcie przetestujesz) lub użyć więcej niż jednego rejestru ( jeden dla każdej kolekcji zestawów, których przecięcie można przetestować). W rzeczywistości rejestr jest przydatny tylko wtedy, gdy chcesz scentralizowanej kontroli, aby upewnić się, że każda para zestawów ma zapisy w indeksie: w niektórych przypadkach może być praktyczne, przy inicjalizacji zestawu S , po prostu rejestrowaćO(|V|)VSad hoc każdy nowy zestaw do indeksów innych zbiorów których przecięciem z S. jesteś zainteresowany.TS