2010-04-01 4 views
3

Мне нужна некоторая приоритетная очередь для хранения пар <key, value>. Значения уникальны, но ключей нет. Я буду выполнять следующие операции (наиболее часто встречающиеся в первую очередь):Вариант очереди приоритетов

  1. случайный ввод;
  2. извлечение (и удаление) всех элементов с наименьшим ключом.
  3. случайное удаление (по значению);

Я не могу использовать std::priority_queue, потому что он поддерживает только удаление головки.

На данный момент я использую unsorted std::list. Вставка выполняется простым нажатием новых элементов на спину (O (1)). Операция 2 сортирует список с list::sort (O (N * logN)) перед выполнением фактического поиска. Удаление, однако, это O (n), что немного дорого.

Любая идея лучшей структуры данных?

+1

«вектор» вместо «списка», если только у вас действительно много элементов. –

+0

Вы хотите гарантировать уникальность 'value' или есть что-то еще, что позаботится об этом? –

+0

Не нужно гарантировать уникальность 'value'. ps .: Я вижу, что вы французский, из-за пространства до '?' :-) –

ответ

0

Итак, я проверил множество вариантов и в итоге получил что-то, основанное на идее Matthieu M..В настоящее время я использую std::map<key_type, std::list<value_type> >, где value_type содержит std::list<value_type>::iterator для себя, что полезно для удаления.

Снятие необходимо проверить, если вектор пуст, что подразумевает запрос map и, возможно, звонок по номеру erase. В худшем случае сложность заключается в том, когда ключи различны, O(logN) для вставки, O(1) для извлечения и O(logN) для снятия. У меня очень хорошие экспериментальные результаты по сравнению с другими альтернативами на моей тестовой машине.

Использование std::vector менее эффективно как с точки зрения теоретической сложности (O (N) в худшем случае для удаления, когда ключи идентичны), так и для экспериментов, которые я выполнял.

1

Если вы используете Visual Studio, у них есть hash_multimap. Я также должен добавить, что Boost имеет неупорядоченный мультимаг, here. Если вам нужна заказанная мультимарма, STL multimap или заказанный множитель STL multiset

+0

Я думаю, что стандартный контейнер должен быть стандартным вариантом в большинстве ситуаций, если ничего другого, потому что это стандартный контейнер. –

+1

pfft, стандартные контейнеры. Если бы люди использовали эти программы, они бы рухнули бы меньше. Тогда такие люди, как я, не должны были объяснять боссу, почему несвязанный раздел кода сталкивается с производством новых функций, и мы просто получили «повезло», которого никогда не было раньше. Тогда нам никогда не приходилось нанимать дорогостоящих консультантов, чтобы писать «лучшие» контейнеры, которые не падали, пока они этого не сделали. Вы видите цикл жизни, который вы пытаетесь разрушить здесь !? – wheaties

+0

См. Мое решение. –

4

Можете ли вы отменить заказ коллекции, т. Е. Сохранить их в <value, key>?

Тогда вы могли бы просто использовать std::map имея O(logn) время для вставки O(n) для удаления (пересекающего всю коллекция) и O(logn) для случайного удаления значения (которое было бы ключом указанной карты).

Если бы вы могли найти map реализации, основанный на хэши вместо деревьев (как std::map) времена было бы еще лучше: O(1), O(n), O(1).

+1

+1. Или нестандартный контейнер 'hash_map'/скоро будет стандартным' std :: unordered_map'. –

+0

Ему нужно заказать и получить ключ, поэтому я не уверен, что это сработает. –

+0

@BillyIneal yes, 'hashmap' даст еще лучшие времена' O (1) ',' O (n) ',' O (1) '. @MarkB он нигде не указал, так почему вы так думаете? – pajton

0

std :: multimap похоже, что вы ищите.

Он сохранит ваши объекты, упорядоченные по ключу, позволит вам получить наименьшее/максимальное значение ключа (begin(), rbegin()) и весь объект с заданным ключом (equal_range, lower_bound, upper_bound).

(EDIT: если у вас есть только несколько пунктов, скажем, менее 30, вы должны также проверить работу только с помощью Deque или вектора)

0

Если я хорошо понял, вы цель производительности иметь быстрые (1) и (3) и (2) не так важны. В этом случае и учитывая, что значения уникальны, почему бы просто не получить std::set<value> и выполнить последовательный поиск (2)? У вас будет O (log n) для (1) и (3) и O (n) для (2). Еще лучше, если ваш STL имеет std::hash_set, вы должны быть близки к O (1) для (1) и (3).

Если вам нужно что-то лучше, чем O (n) для (2), альтернативой может быть набор приоритетных очередей.

+0

Я сказал, что операции были ** наиболее распространены сначала **. Мое начальное решение лучше, чем ваше, потому что его O (1) для вставки. –

+0

Я не думаю, что вы сможете ускорить другие операции, не отказываясь (1), здесь нет никакой магии. Во всяком случае, если у вас есть реализация std :: hash_set, то вставка довольно близка к O (1) –

+1

Ваше решение «Helltone» не синхронизировано, поэтому вы не знаете, лучше ли это. Примечательно, что вы забыли рассмотреть аспекты скорости памяти, такие как кеширование и использование кучи. Если вы не потратите время, вы не можете точно сказать, быстрее ли это. Большой 'O (1)' хуже, чем 'O (log (n))' для небольших значений 'n'. –

4

Когда вам нужно сделать заказ, используйте заказанный контейнер. Нет смысла оплачивать стоимость сортировки позже.

Ваше текущее решение:

  • Вставка O(1)
  • индексирование O(N log N)
  • Удаление O(N) (который так же хорошо, как вы можете получить без сохранения другого индекса там)

Просто используя a std::multi_map, вы могли бы:

  • Вставка O(log N)
  • индексирование O(log N) < - гораздо лучше, не так ли? Нам нужно найти конец диапазона
  • Удаление O(N)

Теперь, вы могли бы сделать немного лучше с std::map< key, std::vector<value> >:

  • Вставка O(log M) где M является число различных ключей
  • Возвращение O(1) (begin ожидается на постоянной основе)
  • Снятие O(N)

Вы не можете надавить на случайное удаление ... если только вы не захотите сохранить там еще один индекс. Например:

typedef std::vector<value_type> data_value_t; 
typedef std::map<key_type, data_value_t> data_t; 

typedef std::pair<data_t::iterator,size_t> index_value_t; 
    // where iterator gives you the right vector and size_t is an index in it 

typedef std::unordered_map<value_type, index_value_t> index_t; 

Но держать этот второй показатель до настоящего времени ошибок, ... и будет сделано за счет других операций! Например, с этой структурой, вы бы:

  • Вставка O(log M) -> сложность вставки в хэш-карта является O(1)
  • индексирование O(N/M) -> нужно отменить индексных все значения в векторе, там в среднем
  • удаление O(N/M)N/M -> нахождение в хэш-карте O(1), разыменования O(1), удаление из вектора O(N/M), потому что нам нужно перенести примерно половину содержимого вектора. Использование list даст O(1) ... но может быть не быстрее (зависит от количества элементов из-за недостатка памяти).

Также учтите, что сложность хэш-карты является амортизированной. Запустите перераспределение, потому что вы перегрузили коэффициент загрузки, и эта конкретная вставка займет очень много времени.

Я бы пошёл с std::map<key_type, std::vector<value_type> > вместо вас. Это лучший удар для доллара.

+0

У boost есть контейнер с несколькими индексами. Поэтому, если вы собираетесь идти по этому маршруту, используйте еще один отлаженный код. –

+0

К сожалению, я не знаю, как получить окончательную структуру с контейнером MultiIndex. Я имею в виду, вы могли бы запросить «multiset» на ключ и «hash_map» на значениях с уникальным ограничением, но это не окончательный проект, который я разработал. Наверное, достаточно хорошо. –

Смежные вопросы