Предположим, что у нас есть множество и каждый член является парой данных и ключей. Нам нужна структура данных, которая бы поддерживала следующие операции:
- Вставьте в ,
- Удалить член (не нужно искать, чтобы найти , например, указывает на члена в ),
- MostFrequent, который возвращает член такой, что - один из самых частых ключей в (обратите внимание, что самый частый ключ не обязательно должен быть уникальным).
Какова будет эффективная реализация этой структуры данных?
Мое решение - это куча ключей и их частот с приоритетом частот, плюс хеш-таблица, в которой хеш-функция отображает элементы с одинаковым ключом в один и тот же слот в хеш-таблице (с указателями от каждой части к другой).
Это может дать для первых двух операций и для третьей (наихудшее время выполнения).
Мне интересно, есть ли более эффективное решение? (или более простое решение с той же эффективностью?)
Ответы:
В модели вычислений, основанной на сравнении, вы можете реализовать приоритетную очередь, используя кучу Фибоначчи вместо обычной кучи. Это даст вам следующие границы: амортизированное время для вставки и O ( log n ) амортизированное время для операций удаления.O(1) O(logn)
Если вы отойдете от модели, основанной на сравнении, и примете модель оперативной памяти, в которой ключи рассматриваются как двоичные строки, каждая из которых содержится в одном или нескольких машинных словах, вы можете реализовать свою очередь приоритетов в . Действительно, вы можете достичь как для операций вставки, так и удаления O ( √o(logn) иO(1)время для операции findMin. Торуп доказал, чтоO(loglogn−−−−−−−√) O(1)
Если мы можем отсортировать ключей за время S ( n ) на ключ, то мы сможем реализовать очередь с приоритетом, поддерживающую find-min в постоянном времени, и обновления (вставка и удаление) за S ( n ) .n S(n) S(n)
Смотри М. Торуп. Эквивалентность между приоритетными очередями и сортировкой, 2002. в Proc. ФОКС 2002
Так как мы можем сортировать в ожидаемое время и линейное пространство, как показаноO(nloglogn−−−−−−−√)
Ю. Хан и М. Торуп. Целочисленная сортировка в ожидаемое время и линейное пространство. в учеб. ФОКС 2002O(nloglogn−−−−−−−√)
оценка доказана.
источник
Вы можете сделать все это за ожидаемое амортизированное время. Суть в том, что нам не нужна полная мощность очереди с приоритетами, поскольку частота клавиш изменяется только на 1 при каждой вставке или удалении.O(1)
Мое решение, приведенное ниже, на самом деле является просто вашим решением с «неэффективной» очередью приоритетов, которая в данном случае работает хорошо: очередь с максимальным приоритетом, реализованная в виде двусвязных списков блоков ключей, имеет O (1) insertMin, deleteMax, removeFromBucket и increaseKey.
Поддерживайте двусвязный список Buckets, где у каждого Bucket есть непустой хэш-набор ключей (который я назову когортой) и положительное целое число (которое я назову ValCount). В сегменте b каждый ключ k в когорте b имеет одинаковое количество уникальных значений, связанных с ним в поддерживаемом вами наборе. Например, если в вашем наборе есть пары (a, яблоко), (a, авокадо), (b, банан), (c, огурец), (d, фрукт дракона), где отдельные буквы - это ключи, а фрукты - значения, то у вас будет два Buckets: один Bucket будет иметь ValCount 2 и Cohort, состоящий только из одного ключа: a. Другой Bucket будет иметь ValCount 1 и Cohort, состоящий из трех ключей b, c и d.
Список Bucket с двойной связью должен быть упорядочен ValCount. Будет важно, чтобы мы могли найти начало и конец списка за время и чтобы мы могли соединить новый Bucket за время O ( 1 ), если мы знаем его соседей. Единственно, я буду называть список Buckets BucketList.O(1) O(1)
В дополнение к BucketList нам понадобится SetMap, представляющий собой ключи отображения хеш-карты для ValueBuckets. ValueBucket - это пара, состоящая из ValueSet (непустой хэш-набор значений) и ненулевого указателя на Bucket. Набор значений, связанный с ключом k, содержит все уникальные значения, связанные с k. Указатель Bucket, связанный с ValueSet, имеет Cohort, равный размеру ValueSet. Bucket, связанный с ключом k в SetMap, также связан с ключом k в BucketList.
В C ++:
Чтобы найти пару ключ-значение максимальной частоты, нам просто нужно взглянуть на заголовок BucketList, найти ключ в когорте, найти этот ключ в SetMap и найти значение в ValueSet его ValueBucket. (Уф!)
Вставить и удалить пары ключ-значение сложнее.
Чтобы вставить или удалить пару ключ-значение, мы сначала вставляем или удаляем ее в SetMap. Это изменит размер ValueSet, поэтому нам нужно изменить Bucket, связанный с ключом. Единственными Buckets, на которые нам нужно обратить внимание, чтобы внести это изменение, будут непосредственные соседи Bucket, в котором раньше находился ключ. Здесь есть несколько случаев, и их, вероятно, не стоит описывать полностью, хотя я был бы рад чтобы уточнить, если у вас все еще есть проблемы.
источник
В худшем случае сложность
доказательство
Который устанавливается с помощью комбинации:
и:
Ссылка
Торуп, Миккель. «Целочисленные приоритетные очереди с уменьшением ключа в постоянном времени и проблема кратчайших путей из одного источника». В материалах тридцать пятого ежегодного симпозиума ACM по теории вычислений, 149–158. STOC '03. Нью-Йорк, штат Нью-Йорк, США: ACM, 2003.
источник