Czy jest jakaś struktura danych, która utrzymuje kolekcję zbioru (zbioru skończonego) obsługującą następujące operacje? Czy doceniony zostanie jakikolwiek podliniowy czas pracy?
- Zainicjuj pusty zestaw.
- Dodaj element do zestawu.
- Biorąc pod uwagę dwa zestawy, zgłoś, czy się przecinają.
data-structures
sets
Dawei Huang
źródło
źródło
Odpowiedzi:
Jeśli każdy zestaw zachowuje zapis istniejących zestawów i masz w sumies>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 zestawyS1,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 jSj Sk . (Dwa zestawy S j i S k współużytkują jedną kopię tego drzewa wyszukiwania.)Sj∩Sk Sj Sk
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) T O(slogs) T Sj T T∩Sj=∅ Dla innych zestawów ; kopiujemy ten sam wskaźnik dla indeksu S j .Sj Sj
Dodanie elementu do zestawuT
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∈V T O(lognT) nT=|T| x S1,S2,… gdzie n = | V |
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∈S T x O(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 .Sj Sk Sj Sk O(logs) Sj Sk Sj∩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 ) , gdziex T T Sj∩T Sj O(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 | .S Sj O(snT) Sj∩T Sj nT=|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.n nT=|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|) V S ad hoc każdy nowy zestaw do indeksów innych zbiorów których przecięciem z S. jesteś zainteresowany.T S
źródło
Istnieją struktury danych, które pozwalają to zrobić w czasie krótszym niż liniowy, nawet w przypadku najgorszych danych wejściowych. Zobacz http://research.microsoft.com/pubs/173795/vldb11intersection.pdf (i odnośniki do dokumentów w nim zawarte).
Jeśli twoje dwa zestawy S i T mają duże przecięcie i masz słownik dla S, wyszukiwanie elementów T w losowej kolejności powinno szybko dać ci wspólny element. Najtrudniejszy jest przypadek, gdy rozmiar przecięcia wynosi 0 lub 1.
źródło
Zwykle wybrany język programowania obsługuje strukturę danych z unikalnymi elementami. Zasadniczo istnieją trzy popularne podejścia: drzewa, skróty i maski bitowe. Elementy drzewa muszą być porównywalne, elementy skrótu muszą być mieszalne, a elementy maski bitowej muszą mieć jakiś sposób konwersji na liczby całkowite.
Zestaw drzew będzie obsługiwał wstawianie do O (log n) i testowanie skrzyżowań w najgorszym przypadku O (n log n).
Zestaw skrótów będzie obsługiwał wstawianie do amortyzacji O (1 * h), gdzie „h” oznacza czas działania algorytmu haszującego, oraz test przecięcia w najgorszym przypadku O (n).
Zestawy bitmask zwykle nie są używane jak zestawy drzewiaste i mieszające.
źródło
Jeśli twoja sprawa pozwala na fałszywie pozytywne odpowiedzi, użyłbym filtra Bloom z pojedynczą funkcją skrótu.
Możesz to zaimplementować w następujący sposób:
Zainicjuj pusty zestaw
Dodaj element do zestawu.
Biorąc pod uwagę dwa zestawy (B1, B2), podaj, czy się przecinają.
Complexity
źródło