Как удалить элемент не сверху из priority_queue?

В моей программе мне нужно удалить элемент из очереди приоритетов, который не находится наверху. Это можно сделать? Если нет, предложите способ сделать это, кроме создания собственной кучи.


person ishan3243    schedule 19.10.2013    source источник
comment
Почему вы специально выбрали приоритетную очередь, если она не поддерживает нужные вам операции? Почему бы вместо этого не выбрать структуру данных, которая поддерживает эти операции, например набор?   -  person Benjamin Lindley    schedule 19.10.2013
comment
@ Бенджамин Линдли, потому что мне нужны оба поведения.   -  person ishan3243    schedule 19.10.2013
comment
Какое поведение? Быстрый доступ к максимальному (или минимальному) элементу? set имеет это. Быстрое удаление произвольных элементов? У set это тоже есть.   -  person Benjamin Lindley    schedule 19.10.2013
comment
@Бенджамин Линдли, о, правда!! я не знал....проверю :)   -  person ishan3243    schedule 19.10.2013
comment
@Benjamin Lindley, но в наборах есть уникальные элементы, и мне нужны повторяющиеся элементы   -  person ishan3243    schedule 19.10.2013
comment
Затем используйте мультисет.   -  person Benjamin Lindley    schedule 19.10.2013
comment
@ Бенджамин Линдли, если я не ошибаюсь, мультимножества реализованы как BST, верно?   -  person ishan3243    schedule 19.10.2013
comment
@ Бенджамин Линдли, я не могу понять, как получить минимальное / максимальное значение в мультисете ..... не могли бы вы сказать мне?   -  person ishan3243    schedule 19.10.2013
comment
*mySet.begin(), *mySet.rbegin(). Так как множество упорядочено, то первый и последний элементы являются наименьшим и наибольшим соответственно.   -  person Igor Tandetnik    schedule 19.10.2013
comment
После сообщения SO, у которого также есть такая же проблема, вы можете сослаться на него. stackoverflow.com/questions/ 3076163/   -  person saroj kumar    schedule 23.10.2013
comment
возможный дубликат Приоритетной очереди STL - удаление элемента   -  person user1937198    schedule 30.05.2015
comment
Можно также использовать набор пар (элемент, i), где i как целое число увеличивается для каждого элемента. Тогда элементы кажутся уникальными. Нужно сохранить значение i в элементе, не влияя на компаратор.   -  person Ant6n    schedule 09.02.2016
comment
@ishan3243 Этот вопрос по-прежнему остается без ответа. Разве ответ Алексма не был точным?   -  person Ted Lyngmo    schedule 13.02.2020


Ответы (5)


Стандартный priority_queue<T> можно настроить путем наследования. У него есть защищенные члены c и comp, на которые можно ссылаться в классе-потомке.

template<typename T>
class custom_priority_queue : public std::priority_queue<T, std::vector<T>>
{
  public:

      bool remove(const T& value) {
        auto it = std::find(this->c.begin(), this->c.end(), value);
        if (it != this->c.end()) {
            this->c.erase(it);
            std::make_heap(this->c.begin(), this->c.end(), this->comp);
            return true;
       }
       else {
        return false;
       }
 }
};

void main()
{
   custom_priority_queue<int> queue;

   queue.push(10);
   queue.push(2);
   queue.push(4);
   queue.push(6);
   queue.push(3);

   queue.remove(6);

   while (!queue.empty())
   {
      std::cout << queue.top();
      queue.pop();

      if (!queue.empty())
      {
        std::cout << ", ";
      }
   }

 }

Вывод:

10, 4, 3, 2

person alexm    schedule 19.04.2016
comment
Вызов make_heap не нужен, так как куча остается упорядоченной. - person klaus triendl; 19.08.2016
comment
@alexm Если я хочу передать объекту собственный компаратор, как мне написать объявление класса? - person Sandeep Tuniki; 17.10.2016
comment
@SandeepTuniki: если вы хотите передать собственный компаратор, объявление класса может выглядеть так: template<typename T, class Container=std::vector<T>, class Compare=std::less<typename Container::value_type>> class custom_priority_queue : public std::priority_queue<T, Container, Compare> - person Manfred Urban; 06.06.2017
comment
@klaustriendl, не могли бы вы объяснить, почему make_heap не нужен, особенно если удаленный элемент оказался вверху / было удалено несколько элементов? Я предполагаю, что make_heap не нужен, только если куча полностью отсортирована, что не так. - person Aelian; 14.12.2018
comment
@Aelian Вы абсолютно правы, и спасибо, что заметили мою ошибку. Кажется, я неправильно понял свойства кучи, поэтому принял отсортированное за структурированное. Поэтому, если вы удаляете элемент, вам нужно реструктурировать кучу. - person klaus triendl; 05.05.2019

Лучшее решение — использовать std::set. Наборы предоставляют методы, которые позволяют использовать его как минимальную/максимальную кучу (или приоритетную очередь).

std::set<int> pq;

//accessing the smallest element(use as min heap)
*pq.begin();

//accessing the largest element (use as max heap)
*pq.rbegin();

Кроме того, наборы также допускают случайное удаление.

//to delete the integer '6'
auto it = pq.find(6);
pq.erase(it);
person Amit Kumar    schedule 17.04.2019
comment
Это решение не будет работать, если элементы могут повторяться. - person Vipul Jain; 28.07.2019
comment
Я думаю, что идея классная, вы можете использовать мультисет для повторяющихся элементов. Но не забудьте обратить внимание, когда вы стираете по значению вместо итератора. - person Po-Jen Lai; 29.10.2019
comment
Для повторяющихся элементов вы можете просто использовать карту. Просто сохраните количество ключей, и когда количество ключей станет равным 0, удалите ключ. Решил эту проблему, используя ту же идею spoj.com/problems/ARRAYSUB. - person Yasser Hussain; 24.07.2021

Небольшой изящный трюк для обработки удалений для очереди с приоритетом STL — используйте другую очередь с приоритетом, скажем, del_pq. Продолжайте вставлять сюда все значения удаления. Когда вы извлекаете значения из исходной очереди с приоритетом, проверьте верхнюю часть del_pq и посмотрите, хотим ли мы ее удалить. Если оно совпадает, удалите значение из исходного priority_queue.

Этот метод реализует способ ленивого удаления значений в нашей исходной очереди приоритетов. Может занимать в два раза больше памяти, но в среднем удаление и вставка остаются O(logN).

person Rishabh Jain    schedule 10.05.2020

Прадип и МАШ жертвуют временем, чтобы реализовать операцию по удалению. Но если для вас важна временная сложность, я предлагаю вам использовать хэш min_heap. В хэш-таблице хранится указатель на значение, а указатели указывают на min_heap. Это означает, что вы можете потратить O(1) времени, чтобы найти значение в min_heap и O(log(n)) для удаления (просеивания или просеивания) элемента.

person Y. Xu    schedule 20.10.2016
comment
Никогда не жертвуйте временем. - person Jon Deaton; 13.12.2017

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

Пусть вы хотите удалить 5-й элемент в priority_queue<type> Q . Затем вы можете сделать это так:

vector<type> tempQ;
int i = 0;
int n = 5;
type t;

// backup n-1 items
while(i < n-1)
{
    tempQ.push_back(Q.top());
    Q.pop();        
    i++;
}

// remove the nth item
Q.pop();

// restore the backed up items
i = 0;
while(i < n-1)
{
    t = tempQ[i++];
    Q.push(t);
}
person Shafi    schedule 27.01.2016
comment
Так в чем же сложность этого метода? Не вижу необходимости использовать временный PQ. почему бы не использовать вектор vector<type> tempQ - person kevin; 25.11.2016
comment
Хороший вопрос, здесь нам просто нужен контейнер. Вместо этого я использую вектор, чтобы улучшить временную сложность. Я обновил ответ. +1 за вашу точку зрения @kevin - person Shafi; 25.11.2016