Ефективна структура даних, що підтримує Insert, Delete та MostFrequent


14

Припустимо, що у нас є множина D і кожен член D - пара даних і ключів. Ми хочемо, щоб структура даних підтримувала наступні операції:

  • Вставте у ,(d,k)D
  • Видалити член , (не потрібно шукати, щоб знайти , наприклад,eee вказує на члена в D ),
  • MostFrequent, який повертає член таким, що e . k e y - одна з найчастіших клавіш D (зауважте, що найчастіша клавіша не повинна бути унікальною).eDe.keyD

Що було б ефективною реалізацією цієї структури даних?

Моє рішення - це купа ключів та їх частот із пріоритетом частот плюс хеш-таблиця, де хеш-функція відображає членів тим самим ключем до того ж слоту в хеш-таблиці (з покажчиками від кожної частини до іншої).

Це може дати для перших двох операцій і Θ ( 1 ) для третього (найгірший час виконання).Θ(lgn)Θ(1)

Мені цікаво, чи є більш ефективне рішення? (чи більш просте рішення з однаковою ефективністю?)


Ви можете використовувати просте збалансоване двійкове дерево пошуку замість хеш-таблиці, якщо хочете.
Джо

Таблиця хешу використовує багато унсарійного простору, я б запропонував пріоритетну чергу. Це дало б вам однакову складність у вставці та видаленні, але складність пам'яті була б кращою.
Bartosz Przybylski

@Joe, використання BST замість хеш-таблиці зробить операції MostFrequent менш ефективними, але це може бути розумним компромісом для пам'яті.
Каве

2
Якщо використовувати лише порівняння, принаймні один з Insert / MostFrequent повинен бути амортизований через нижні межі для проблеми розрізнення елементів. Ω(журналн)
Ар'ябхата

1
У потоковій моделі також є кілька цікавих структур. springerlink.com/content/t17nhd9hwwry909p
Джо

Відповіді:


7

У моделі обчислення на основі порівняння ви можете реалізувати чергу пріоритетів, використовуючи купу Фібоначчі замість звичайної купи. Це дасть вам наступні межі: амортизований час для вставки та O ( log n ) амортизований час для операцій видалення.O(1)O(logn)

Якщо ви відходите від моделі на основі порівняння і приймаєте модель оперативної пам’яті, де ключі розглядаються як двійкові рядки, кожен з яких міститься в одному або декількох машинних словах, ви можете реалізувати свою чергу пріоритетів в . Дійсно, ви можете досягти як операцій вставки, так і видалення O ( o(logn)таO(1)час операції findMin. Торпуп це довівO(loglogn)O(1)

Якщо ми можемо сортувати клавіш за часом S ( n ) за ключем, тоді ми можемо реалізувати пріоритетну чергу, що підтримує find-min у постійному часі, та оновлення (вставлення та видалення) у S ( n ) час.nS(n)S(n)

Див. М. Торпуп. Еквівалентність між пріоритетними чергами та сортуванням, 2002. у Proc. FOCS 2002

Оскільки ми можемо сортувати в очікуваний час та лінійний простір, як показано наO(nloglogn)

Ю. Хан та М. Торпуп. Сортування цілого числа в очікуваний час та лінійний простір. у Зб. FOCS 2002O(nloglogn)

межу доведено.


1

Ви можете зробити все це в очікуваний амортизований час . Основна хитрість полягає в тому, що нам не потрібна повна потужність черги пріоритету, оскільки частота ключів змінюється лише на 1 під час кожного вставки або видалення.O(1)

Моє рішення нижче - це лише ваше рішення з "неефективною" чергою пріоритетів, яка, як правило, спрацьовує в цьому випадку: максимум черги пріоритетів, реалізований як подвійно пов'язані списки відро ключів, має O (1) insertMin, deleteMax, removeFromBucket та збільшення Ключ.


Підтримуйте подвійно пов'язаний список Buckets, де кожен Bucket має не порожній хеш-набір ключів (який я буду називати когортою) та додатне ціле число (що я буду називати ValCount). У Bucket b кожен ключ k у когорті b має однакову кількість унікальних значень, пов'язаних з ним, у наборі, який ви підтримуєте. Наприклад, якщо у вашому наборі є пари (a, яблуко), (a, авокадо), (b, банан), (c, огірок), (d, плід дракона), де однією буквою є ключі, а плоди - Значення, тоді у вас було б два відра: Одне відро матиме ValCount 2 та когорту, що складається лише з одного ключа: a. Інший відро матиме ValCount 1 та когорту, що складається з трьох клавіш b, c та d.

Подвійно пов'язаний список Bucket повинен вестись упорядкованим ValCount. Важливо, що ми зможемо знайти голову та хвіст списку в час і що ми можемо сплести в новому відрі в O ( 1 ) час, якщо знаємо його сусідів. Немислено я назву список Buckets BucketList.O(1)O(1)

На додаток до BucketList нам знадобиться SetMap, який є ключами хеш-карти зіставленням у ValueBuckets. ValueBucket - це пара, що складається з ValueSet (не порожній хеш-набір значень) та ненульовий вказівник на Bucket. ValueSet, пов'язаний з ключем k, містить усі унікальні значення, пов'язані з k. Вказівник Bucket, пов'язаний із ValueSet, має когорту, рівну розміру ValueSet. Bucket, асоційований з ключем k у SetMap, також асоціюється з ключем k у BucketList.

В C ++:

struct Bucket {
    unsigned ValCount;
    unordered_set<Key> Cohort;
    Bucket * heavier;
    Bucket * lighter;
};
Bucket * BucketListHead;
Bucket * BucketListTail;

struct ValueBucket {
  unordered_set<Value> ValueSet;
  Bucket * bucket;
};
unordered_map<Key, ValueBucket> SetMap;

Щоб знайти максимум частоти пари ключ-значення, нам просто потрібно подивитися на голову BucketList, знайти ключ у когорті, знайти цей ключ у SetMap та знайти значення у ValueSet його ValueBucket. (феу!)

Вставлення та видалення пар ключ-значення складніше.

Щоб вставити або видалити пару ключів-значень, спочатку вставляємо або видаляємо її у SetMap Це змінить розмір ValueSet, тому нам потрібно змінити Bucket, пов'язаний з ключем. Єдині відра, на які нам потрібно буде звернути увагу, щоб внести цю зміну, - це безпосередні сусіди Ковша, ключовим для якого був. У цьому випадку є декілька випадків, і вони, мабуть, не варто в повному обсязі викладати, хоча я буду радий уточнити, якщо у вас все ще виникають проблеми.


Спасибі. Насправді у нас була схожа ідея щодо рішення, але в цьому була проблема. Я повинен перевірити, в чому була проблема, оскільки я її зараз не пригадую. Я перевіряю це більш уважно на наступному тижні, щоб побачити, чи дозволяє уникнути проблеми, яка мала наше рішення.
Каве

Ось ще один спосіб подумати над моїм рішенням: це справді лише ваше рішення з "неефективною" чергою пріоритетів, яка, як правило, працює в цій справі. Максимальна пріоритетна черга, реалізована у вигляді подвійно пов'язаних списків відро ключів, має O (1) insertMin, deleteMax, removeFromBucket та збільшенняKey.
jbapple

Напевно, найефективніший спосіб (з точки зору гіршого випадку) підтримувати відображення з клавіш для ValueBuckets, ймовірно, дерево пошуку.
Рафаель

Рафаель - я не впевнений, у що ти потрапляєш. Ви хочете сказати, що хеш-таблиці на практиці дорогі, або що вони мають погану ефективність у гіршому випадку, чи щось третє?
jbapple

-3

Найгірші складності

О(1)

О(1)

О(1)

О(журналжурналн)

О(н)

Доказ

[0,N) О(лог лог мiн{н,N})

Який встановлюється за допомогою комбінації:

τ(н,N) н[0,N) τ(н,N)τ(N,N)τ

і:

ТЕОРЕМА 6. Теорема 6. Ми можемо реалізувати пріоритетну чергу, за допомогою якоїн цілі клавіші в діапазоні [0,N)у лінійному просторі, що підтримує fi nd-min , вставляти та відмикати клавішу в постійний час та видаляти вО(1+лог лог н-лог лог q) час на ключ рангу q.

Довідково

Торпуп, Міккель. "Чергові черги з пріоритетним зменшенням за постійний час та проблема найменших коротких шляхів з єдиним джерелом". У працях Тридцять п’ятого щорічного симпозіуму ACM з теорії обчислень, 149–158. STOC '03. Нью-Йорк, Нью-Йорк, США: ACM, 2003.


Зауважте, що у всіх чергових пріоритетах тривіально переходити до структури, що підтримує 'get-min' та 'extra-min' до структури, що підтримує 'get-max' та 'extra-max'.
AT

Ping ...: @Kaveh
AT
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.