STL Priority Queue — удаление элемента

Я хочу реализовать систему очередей по таймеру, используя адаптер контейнера C++ STL priority_queue.

Моя проблема в том, что я хочу иногда отменять таймер, однако нет интерфейсов, которые позволяют мне легко удалить элемент в priority_queue, который не является верхним элементом.

Какие-либо предложения?.

Спасибо за помощь.


person Umbungu    schedule 19.06.2010    source источник


Ответы (6)


Однажды у меня был точно такой же сценарий, и я сделал следующее:

  • структура, которую я сохранил в std::priority_queue, содержала только время для сортировки и индекс для std::vector<Handler> (в моем случае Handler было boost::function, но также могло быть указателем на интерфейс или функцию)
  • при добавлении таймера я находил свободный индекс в векторе обработчиков1 и сохранял обработчик по этому индексу. Сохраните индекс и время в priority_queue. Верните индекс клиенту в качестве токена для отмены
  • чтобы отменить таймер, передайте индекс, полученный при его добавлении. Очистите обработчик по этому индексу (для boost::function вызовите clear(), если используете указатели, установите его равным нулю)
  • когда пришло время вызвать таймер, получить его индекс обработчика из очереди приоритетов и проверить вектор обработчиков - если обработчик в этой позиции пуст ()/NULL, таймер был отменен. Отметьте этот индекс обработчика как свободный2.

1 Чтобы быстро найти свободный индекс, я использовал отдельные std::stack индексов. При добавлении таймера, когда этот стек пуст, добавьте в конец вектора; в противном случае вытащите верхний индекс и используйте его.

2 Вот момент, когда вы помещаете индекс в стек свободных индексов

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

person sbk    schedule 19.06.2010
comment
Я тоже делал что-то подобное. Это полезный шаблон. - person Tyler McHenry; 20.06.2010
comment
Интригующий. Но я не вижу, как вы обрабатываете случай, когда таймер отменен, но на самом деле он уже выполнен. Вы просто предоставляете вызывающему коду стирать любые дескрипторы таймера, когда этот таймер срабатывает? - person Lightness Races in Orbit; 24.01.2016
comment
Я думаю, что мои структуры с очередями будут хранить boost::shared_ptr<some_detail_type>, который функционирует как дескриптор таймера. Затем любой может войти в этот дескриптор и установить отменённый флаг, который будет поднят, когда наступит время таймера. - person Lightness Races in Orbit; 24.01.2016

Боюсь, STL priority_queue не предлагает такой функциональности. Вы можете написать свой собственный класс кучи (что не так сложно). Вы даже можете использовать функции std::xxx_heap с помощью таких грязных трюков:

delete_heap(iterator to_delete, iterator heap_begin, iterator heap_end)
{
  to_delete->key = something that would compare less to everything; // make sure it gets to the top in the next step
  std::push_heap(heap_begin, to_delete+1);
  std::pop_heap(heap_begin, heap_end);
}

что позволит вам O(log n) удалить.

person jpalecek    schedule 19.06.2010
comment
Интригующий. Сколько данных будет скопировано функцией? - person Dummy00001; 19.06.2010

Несмотря на то, что говорят некоторые другие ответы, можно получить доступ к базовому контейнеру любого стандартного адаптера контейнера, включая priority_queue, поскольку контейнер отображается как защищенный член с именем c. Вы можете либо наследовать от priority_queue и расширить интерфейс, либо использовать грязные приемы, такие как deque-from-a-stack/3034221#3034221">это, чтобы временно получить доступ к обычному файлу priority_queue.

person Mike Seymour    schedule 19.06.2010
comment
Интересно знать, что член контейнера c на самом деле стандартизирован. - person sstn; 02.09.2014

Моя проблема в том, что я хочу иногда отменять таймер, однако нет интерфейсов, которые позволяют мне легко удалить элемент в priority_queue, который не является верхним элементом.

Если отмена таймера происходит часто, вам нужно использовать какую-то другую структуру. std::map тоже не так уж плох, хотя стоимость delete_min возрастет.

Если отмена таймера происходит редко, то пометка элемента как удаленного (и игнорирование его во время ::pop) может помочь.

person Dummy00001    schedule 19.06.2010

Контейнер Priority_queue STL специально разработан таким образом, что доступен только верхний элемент, поэтому, если вам нужно удалить элементы, не являющиеся верхними, вам придется найти другой класс для использования.

person Amber    schedule 19.06.2010

У меня было такое же требование. Если у вас есть возможность изменить контейнер, проблему может решить std:: set (дубликаты не допускаются) или std::multiset.

Оба упорядочены и могут стирать элемент, логарифмический по размеру контейнера (подробности см. в документации). Для помощи в удалении в мультинаборе вы можете увидеть это.

Посмотрите разницу std::set vs std::priority_queue, прежде чем принять решение.

person n00buser    schedule 12.11.2016