2009-03-16 2 views
24

У меня есть priority_queue некоторого объекта:Как сделать эффективное обновление приоритета в STL priority_queue?

typedef priority_queue<Object> Queue; 
Queue queue; 

Время от времени, приоритет одного из объектов может измениться - мне нужно, чтобы иметь возможность обновить приоритет этого объекта в очереди в эффективной путь. В настоящее время я использую этот метод, который работает, но кажется неэффективным:

Queue newQueue; 
while (!queue.empty()) 
{ 
    Object obj=queue.top(); 
    queue.pop(); 

    if (priorityHasChanged(obj)) 
    newQueue.push_back(Object(new_priority)); 
    else 
    newQueue.push_back(obj); 
} 

newQueue.swap(queue); // this only works because I actually subclassed the priority_queue 
       // class and exposed a swap method that swaps in the container 

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

  • извлечь из экземпляра с измененным приоритетом и вставить новый с новым значением приоритета
  • обновления экземпляра с измененным приоритетом, а затем обновить очередь таким образом, что он правильно отсортирован

Каков наилучший способ для этого?

ответ

7

Я думаю, вам не повезло со стандартной очередью приоритетов, потому что вы не можете попасть в базовый deque/vector/list или что-то еще. Вам нужно реализовать свои собственные - это не так сложно.

+4

Неправильно - стандартная очередь приоритетов хранит контейнер в качестве защищенного члена 'c' (см. [Reference] (http://en.cppreference.com/w/cpp/container/priority_queue)). Вы можете получить из очереди приоритетов и получить доступ к контейнеру в производном классе. – 2013-08-28 07:37:14

+0

@ user283145 уверен, что вы можете получить доступ к основной куче с 'c', но поскольку вы не можете найти индекс ключа в O (logn), вы не можете сделать обновление приоритета в O (lgn), которое действительно несчастная часть. –

0

Возможно, вы захотите взглянуть на replace_if с примером here.

+1

Извините, это неприменимо, класс priority_queue не имеет итераторов произвольного доступа (или любого другого типа). http://www.cplusplus.com/reference/stl/priority_queue/ –

+1

Большое значение. Я кое-что узнаю из этого. – dubnde

4

Самый простой способ сделать это с помощью STL (я знаю) - удалить запись, обновить ее приоритет и затем вставить ее повторно. Выполнение этого довольно медленно, используя std :: priority_queue, однако вы можете сделать то же самое с std :: set. К сожалению, вы должны быть осторожны, чтобы не изменять приоритет объекта, когда он находится в наборе.

Я реализовал класс mutable_priority_queue на основе склеивания std :: multimap (для сопоставления приоритетов и значений) и std :: map (для сопоставления значений для приоритетов), который позволяет вставлять/удалять элементы, а также обновлять существующие значения в логарифмическом времени. Вы можете получить код и пример его использования. here

+1

Вы не можете удалить запись в произвольной позиции в очереди приоритетов stl - вы можете получить доступ только к верхнему элементу. –

+0

@James: Спасибо за код. Вот несколько советов: (1) Определение шаблона для mutable_priority_queue очевидно (приоритет, ключ), однако оператор insert (ключ, приоритет). Они должны быть согласованными, предпочтительно с обоими (ключ, приоритет). Итератор должен также иметь «первый», являющийся ключевым, а второй - приоритетом. (2) Вы должны предоставить getPriority (const key), чтобы получить текущий приоритет. Это общее требование для алгоритмов, где вам нужно получить приоритет для элемента, который не обязательно находится наверху, чтобы повысить приоритет вверх или вниз. – stackoverflowuser2010

6

Соответствующая структура данных называется «Куча Фибоначчи». Но вам нужно реализовать его самостоятельно. Вставка/Обновления O (1) ExtractMin is O (logn)

32

Я могу предложить 2 варианта решения проблемы, хотя и не выполняет реального обновления.

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

    Таким образом вы откладываете удаление обновленного элемента до тех пор, пока он не достигнет вершины. Я заметил, что этот подход используется ведущими программистами, реализующими алгоритм Дейкстры.

  2. set. Он также сортируется, поэтому вы можете извлечь наибольший элемент в логарифмическом времени. Вы также можете удалить устаревший элемент, прежде чем вставлять его снова. Таким образом, все еще невозможна операция обновления, но удаление и повторное вхождение выполнимы.

Похоже, что сложность обоих подходов одинакова.

+1

«Я заметил, что этот подход используется ведущими программистами, реализующими алгоритм Дейкстры». Было бы намного понятнее, если бы программисты упомянули об этом факте. Спасибо, что указали! – sonofrage

+0

@sonofrage Как именно вы «проверяете, содержит ли оно актуальное значение» ??? – dimitris93

+0

Обычно данные в очереди являются избыточными. Таким образом, вы можете сравнить значение из очереди со значением в другом объекте данных, возможно, в таблице. Если значение отличается, оно должно быть отброшено. – Jarekczek

0

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

Я думаю, что вам лучше использовать set, так как Jarekczek сказал или использует this solution (используя make_heap).

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

  • Нет связанных вопросов^_^