Якщо кожен набір підтримує запис про те, що існують інші набори, і у вас є загальна кількість наборів, ви можете легко перетворити будь-яку структуру даних для колекції ( наприклад, двійкові дерева пошуку тощо ) в ту, де ви можете отримати пошук елемент перетину двох множин у часі O ( log s ) .s>0O(logs)
Кожен набір повинен мати унікальний ідентифікатор від деякого повністю упорядкованого набору. Якщо ви чітко називаєте свої набори тоді ідентифікатором може бути просто індекс.S1,S2,…
Ви повинні реалізувати "реєстр" наборів; структура даних, яка підтримує набір усіх визначених вами наборів. Реєстр повинен бути реалізований як структура даних дерева дерева пошуку, щоб забезпечити просте пошуку ( наприклад, якщо ви хочете видалити набір) та лінійне проходження часу наборів.
Кожен набір також підтримує "індекс" кожного з інших наборів - не їх копію , а структуру даних, яка індексується мітками інших наборів. Цей індекс буде використовуватися для підтримки для кожного набору S k , двійкового дерева пошуку всіх елементів S j ∩ S k . (Два набори S j і S k ділять одну копію цього дерева пошуку.)SjSkSj∩SkSjSk
Ініціалізація
Ініціалізація множини складається з O ( 1 ) операцій з ініціалізації дерева його елементів, O ( s ) операцій під час ініціалізації (копіювання з реєстру) індексу для набору T , а O ( s log s ) операції під час проходження реєстру для додавання T до індексів кожного з інших наборів S j . В індексі T ми створюємо дерева пошуку, що представляють T ∩ S j = ∅T=∅O(1)O(s)TO(slogs)TSjTT∩Sj=∅для інших множин ; ми копіюємо той же покажчик для індексу S j .SjSj
Додавання елемента до набору T
Додавання деякого до множини T вимагає часу O ( log n T ), як звичайно, де n T = | Т | . Ми також перевіряємо приналежність x у кожному з інших наборів S 1 , S 2 , … , що вимагає часу O ( log n S 1 + log n S 2 + ⋯ ) ⊆ O ( s log nx∈VTO(lognT)nT=|T|xS1,S2,… де n = | V | - розмір Всесвіту (або найбільшого набору S j ) і s - кількість наборів у реєстрі. Для кожного безлічі S J такещо х ∈ S J ,також вставка х в індекс для безлічі S J ∩ T . Для кожного такого набору S J , це займає O ( LOG сек + лог н T ) час, щоб подивитися S J
O(lognS1+lognS2+⋯)⊆O(slogn),
n=|V|SjsSjx∈SjxSj∩TSjO(logs+lognT)Sjв індекс
і вставити
x у
S j ∩ T ; для всіх безлічі
S 1 , S 2 , ... для цього потрібен час
O ( s log s + s log n T ) . Якщо припустити, що кількість множин
S j набагато менша за розмір Всесвіту
V (тобто, якщо припустити, що
s ≪ n ), то загальний час для вставки елемента - це
O ( s log n )TxSj∩TS1,S2,…O(slogs+slognT)SjVs≪nO(slogn).
Якщо ви не допускаєте дублікати в наборах, ми можемо заощадити час в тому випадку, коли вже за рахунок відмови від тестування членства і вставок для інших наборів Т . "Вставка" у випадку, коли x вже присутній, тоді потрібен час лише O ( log n T ) .x∈STxO(lognT)
Випробування на перехресті
Індекс кожного набору підтримується саме для того, щоб дати можливість швидко оцінити, чи перетинаються два набори і S k . Для безлічі S J , просто шляхом перевірки його індексу для безлічі S до , ми не можемо визначити тільки під час O ( журнал ами ) або НЕ ˙s J перетинає S до , але ми можемо також отримати бінарне дерево , що містить повний набір S j ∩ S k .SjSkSjSkO(logs)SjSkSj∩Sk
Видалення елемента
Щоб видалити елемент з множини T , ми видаляємо його не тільки з дерева пошуку для самого T , але з кожного з перетинів S j ∩ T для множин S j у своєму індексі. Для цього потрібен час O ( s log n T ) , де n T = | Т | .xTTSj∩TSjO(slognT)nT=|T|
Встановити видалення
Через накладні пошуки реєстру, якщо у вас є безліч наборів, можливо, буде бажано видалити набори, як тільки вони більше не потрібні. Пройшовши весь реєстр, ми можемо видалити з індексу всіх інших наборів S j за часом O ( s n T ) , домінуючи на витрату на видалення дерева пошуку, що представляє S j ∩ T для кожного з інших наборів S j , де n T = | Т | .SSjO(snT)Sj∩TSjnT=|T|
Зауваження
Якщо ви плануєте впровадити лише постійну кількість наборів, то наведені вище періоди виконання зменшуються до:
ініціалізація: O(1)
Вставка елемента: O(logn)
випробування на перехресті (та пошук перехрестя): O(1)
видалення елемента: O(lognT)
встановити видалення: O(nS)
де - розмір найбільшого набору в реєстрі, а n T = | Т | для набору T, над яким ви працюєте.nnT=|T|T
Якщо ви очікуєте, що у вас є набори , де V - ваша всесвіт, вам може знадобитися інша структура даних, якщо ви хочете, щоб ці операції працювали в підлінійний час. Однак якщо у вас є пари множин, перетин яких ви знаєте, що ви ніколи не перевірите, ви, можливо, зможете зменшити розмір індексу для наборів (не включаючи жодних наборів, перетин яких ви протестуєте) або використовувати більше одного реєстру ( по одному для кожної колекції наборів, перетин яких ви можете протестувати). Насправді, реєстр корисний лише у тому випадку, якщо ви хочете централізовано контролювати, щоб кожна пара наборів записувала один одного в індексі: це може бути практично в деяких випадках при ініціалізації набору S просто для записуO(|V|)VSTS