Структура даних для встановлення перетину?


21

Чи є структура даних, яка підтримує набір (з кінцевим набором наземних), що підтримує наступні операції? Буде оцінено будь-який підлінійний час роботи?

  1. Запустіть порожній набір.
  2. Додайте елемент до набору.
  3. Дано два набори, повідомте, чи перетинаються вони.

1
Це дуже загальне питання, оскільки будь-яка структура даних може підтримувати ці операції з обмеженою областю. Чи можете ви бути трохи більш конкретним? Напр. Яка складність вам потрібна, що ви готові принести в жертву, щоб отримати операції тощо
Bartosz Przybylski

Відповіді:


13

Якщо кожен набір підтримує запис про те, що існують інші набори, і у вас є загальна кількість наборів, ви можете легко перетворити будь-яку структуру даних для колекції ( наприклад, двійкові дерева пошуку тощо ) в ту, де ви можете отримати пошук елемент перетину двох множин у часі O ( log s ) .s>0O(logs)

  • Кожен набір повинен мати унікальний ідентифікатор від деякого повністю упорядкованого набору. Якщо ви чітко називаєте свої набори тоді ідентифікатором може бути просто індекс.S1,S2,

  • Ви повинні реалізувати "реєстр" наборів; структура даних, яка підтримує набір усіх визначених вами наборів. Реєстр повинен бути реалізований як структура даних дерева дерева пошуку, щоб забезпечити просте пошуку ( наприклад,  якщо ви хочете видалити набір) та лінійне проходження часу наборів.

  • Кожен набір також підтримує "індекс" кожного з інших наборів - не їх копію , а структуру даних, яка індексується мітками інших наборів. Цей індекс буде використовуватися для підтримки для кожного набору S k , двійкового дерева пошуку всіх елементів S jS k . (Два набори S j і S k ділять одну копію цього дерева пошуку.)SjSkSjSkSjSk

Ініціалізація

Ініціалізація множини складається з O ( 1 ) операцій з ініціалізації дерева його елементів, O ( s ) операцій під час ініціалізації (копіювання з реєстру) індексу для набору T , а O ( s log s ) операції під час проходження реєстру для додавання T до індексів кожного з інших наборів S j . В індексі T ми створюємо дерева пошуку, що представляють T S j = T=O(1)O(s)TO(slogs)TSjTTSj=для інших множин ; ми копіюємо той же покажчик для індексу 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 nxVTO(lognT)nT=|T|xS1,S2, де n = | V | - розмір Всесвіту (або найбільшого набору S j ) і s - кількість наборів у реєстрі. Для кожного безлічі S J такещо х S J ,також вставка х в індекс для безлічі S JT . Для кожного такого набору S J , це займає O ( LOG сек + лог н T ) час, щоб подивитися S J

O(lognS1+lognS2+)O(slogn),
n=|V|SjsSjxSjxSjTSjO(logs+lognT)Sjв індекс і вставити x у S jT ; для всіх безлічі S 1 , S 2 , ... для цього потрібен час O ( s log s + s log n T ) . Якщо припустити, що кількість множин S j набагато менша за розмір Всесвіту V (тобто, якщо припустити, що s n ), то загальний час для вставки елемента - це O ( s log n )TxSjTS1,S2,O(slogs+slognT)SjVsnO(slogn).

Якщо ви не допускаєте дублікати в наборах, ми можемо заощадити час в тому випадку, коли вже за рахунок відмови від тестування членства і вставок для інших наборів Т . "Вставка" у випадку, коли x вже присутній, тоді потрібен час лише O ( log n T ) .xSTxO(lognT)

Випробування на перехресті

Індекс кожного набору підтримується саме для того, щоб дати можливість швидко оцінити, чи перетинаються два набори і S k . Для безлічі S J , просто шляхом перевірки його індексу для безлічі S до , ми не можемо визначити тільки під час O ( журнал ами ) або НЕ ˙s J перетинає S до , але ми можемо також отримати бінарне дерево , що містить повний набір S jS k .SjSkSjSkO(logs)SjSkSjSk

Видалення елемента

Щоб видалити елемент з множини T , ми видаляємо його не тільки з дерева пошуку для самого T , але з кожного з перетинів S jT для множин S j у своєму індексі. Для цього потрібен час O ( s log n T ) , де n T = | Т | .xTTSjTSjO(slognT)nT=|T|

Встановити видалення

Через накладні пошуки реєстру, якщо у вас є безліч наборів, можливо, буде бажано видалити набори, як тільки вони більше не потрібні. Пройшовши весь реєстр, ми можемо видалити з індексу всіх інших наборів S j за часом O ( s n T ) , домінуючи на витрату на видалення дерева пошуку, що представляє S jT для кожного з інших наборів S j , де n T = | Т | .SSjO(snT)SjTSjnT=|T|

Зауваження

Якщо ви плануєте впровадити лише постійну кількість наборів, то наведені вище періоди виконання зменшуються до:

  • ініціалізація: O(1)

  • Вставка елемента: O(logn)

  • випробування на перехресті (та пошук перехрестя): O(1)

  • видалення елемента: O(lognT)

  • встановити видалення: O(nS)

де - розмір найбільшого набору в реєстрі, а n T = | Т | для набору T, над яким ви працюєте.nnT=|T|T

Якщо ви очікуєте, що у вас є набори , де V - ваша всесвіт, вам може знадобитися інша структура даних, якщо ви хочете, щоб ці операції працювали в підлінійний час. Однак якщо у вас є пари множин, перетин яких ви знаєте, що ви ніколи не перевірите, ви, можливо, зможете зменшити розмір індексу для наборів (не включаючи жодних наборів, перетин яких ви протестуєте) або використовувати більше одного реєстру ( по одному для кожної колекції наборів, перетин яких ви можете протестувати). Насправді, реєстр корисний лише у тому випадку, якщо ви хочете централізовано контролювати, щоб кожна пара наборів записувала один одного в індексі: це може бути практично в деяких випадках при ініціалізації набору S просто для записуO(|V|)VSTS


6

Існують структури даних, які дозволяють зробити це за менший за лінійний час навіть для найгірших даних. Див. Http://research.microsoft.com/pubs/173795/vldb11intersection.pdf (та посилання на документи там).

Якщо ваші два набори S і T мають велике перетин і у вас є словник для S, пошук елементів T у випадковому порядку повинен швидко дати вам загальний елемент. Найскладніший випадок, коли розмір перетину дорівнює 0 або 1.


3

Зазвичай ваша мова програмування на вибір підтримуватиме структуру даних з унікальними елементами. Загалом є три популярні підходи: Дерева, Хеші та Бітмаски. Елементи дерева повинні бути порівнянними, елементи Hash повинні бути доступними, а елементи Bitmask повинні мати певний спосіб перетворення в цілі числа.

Набір дерев підтримуватиме вставлення в O (log n) та тестування перетину в Worst Case O (n log n).

Хеш-набір підтримуватиме вставку в Amortized O (1 * h), де 'h' - час роботи алгоритму хешування, і тест перетину в Worst Case O (n).

Набори біткої маски зазвичай не використовуються, як набори дерев і хешів.


2
Це була б гідна відповідь на переповнення стека , але тут ми хотіли б трохи детальніше про те, як і чому це працює.
Рафаель

3

Якщо ваш випадок дає хибні позитивні відповіді, я б використовував Bloom Filter з однією хеш-функцією.

Ви можете реалізувати його наступним чином:

Запустіть порожній набір

  • Б = бітовий масив н біт, усі встановлені на 0. (н should be chosen according to the number of possible elements)

Add an element to a set.

  • B[hash(element)]=1

Given two sets(B1,B2), report whether they intersect.

  • check if B1 AND B2 = 0

Complexity

  • If n is not too big, all operations are O(1).
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.