64

In my program I need to delete an element from a priority queue that is not at the top. Can that be done? If not, please suggest a way to do so except creating your own heap.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
ishan3243
  • 1,870
  • 4
  • 30
  • 49
  • 9
    Why a have you specifically chosen a priority queue if it doesn't support the operations you want? Why not instead choose a data structure which *does* support those operations, like a set? – Benjamin Lindley Oct 19 '13 at 15:11
  • @Benjamin Lindley because i need both behaviors. – ishan3243 Oct 19 '13 at 15:13
  • 9
    Which behaviors? Fast access to the max (or min) element? `set` has that. Quick removal of arbitrary elements? `set` has that too. – Benjamin Lindley Oct 19 '13 at 15:14
  • @Benjamin Lindley oh really!! i didn't know that....i'll check :) – ishan3243 Oct 19 '13 at 15:15
  • @Benjamin Lindley but sets have unique elements and i need duplicate elements – ishan3243 Oct 19 '13 at 15:18
  • 13
    Then use a [multiset](http://en.cppreference.com/w/cpp/container/multiset). – Benjamin Lindley Oct 19 '13 at 15:19
  • @Benjamin Lindley if i am not wrong multisets are implemented as BST right? – ishan3243 Oct 19 '13 at 15:35
  • @Benjamin Lindley i am not able to find out how to get the min/max in multiset.....could you please tell me? – ishan3243 Oct 19 '13 at 15:46
  • 5
    `*mySet.begin()`, `*mySet.rbegin()`. Since a set is ordered, the first and last elements are the smallest and the largest, correspondingly. – Igor Tandetnik Oct 19 '13 at 18:04
  • 2
    possible duplicate of [STL Priority Queue - deleting an item](http://stackoverflow.com/questions/3076163/stl-priority-queue-deleting-an-item) – user1937198 May 30 '15 at 17:04
  • 1
    Following SO post also having same problem you can refer it. http://stackoverflow.com/questions/3076163/stl-priority-queue-deleting-an-item/3076722#3076722 – saroj kumar Oct 23 '13 at 02:36
  • 1
    One could also use a set of pairs (item, i), where i as an integer increased for every item. Then the elements appear unique. One has to store the i value in the item without it affecting the comparator. – Ant6n Feb 09 '16 at 19:46
  • @ishan3243 This question still shows up as unanswered. Wasn't alexm's answer spot on? – Ted Lyngmo Feb 13 '20 at 19:00

5 Answers5

65

The standard priority_queue<T> can be customized through inheritance. It has protected members c and comp that can be referenced in a descendant class.

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()) {
              return false;
          }
          if (it == this->c.begin()) {
              // deque the top element
              this->pop();
          }    
          else {
              // remove element and re-heap
              this->c.erase(it);
              std::make_heap(this->c.begin(), this->c.end(), this->comp);
         }
         return true;
     }
};

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 << ", ";
      }
   }

 }

Output:

10, 4, 3, 2

alexm
  • 6,854
  • 20
  • 24
  • 3
    The call to `make_heap` isn't necessary since the heap remains ordered. – klaus triendl Aug 19 '16 at 14:22
  • @alexm If I want to pass a custom comparator to the object, how do I write the class declaration? – Sandeep Tuniki Oct 17 '16 at 09:47
  • 2
    @SandeepTuniki: If you want to pass a custom comparator, the class declaration could look like this: `template, class Compare=std::less> class custom_priority_queue : public std::priority_queue` – Manfred Urban Jun 05 '17 at 22:11
  • 7
    @klaustriendl, could you explain why `make_heap` is unnecessary, especially if the removed item happen to be at the top / multiple items were removed? My guess is that `make_heap` is unnecessary only if heap is fully sorted which is not the case. – Aelian Dec 14 '18 at 18:26
  • 3
    @Aelian You are absolutely right, and thank you for detecting my mistake. It seems I misunderstood the properties of a heap, so I mistakened "sorted" for "structured". So if you remove an element you need to restructure the heap. – klaus triendl May 05 '19 at 18:54
  • you could check if "it" equals this->c.begin(), if not, no need to call make_heap after erase() – foddex Jun 13 '22 at 13:37
  • @foddex : added special case when the found item is on the top of the queue. – alexm Jul 26 '22 at 06:00
46

The best solution is to use std::set. Sets provide methods which allow it to be used both as a min/max heap (or a priority queue).

std::set<int> pq;

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

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

Furthermore sets also allow random deletion.

//to delete the integer '6'
auto it = pq.find(6);
pq.erase(it);
Amit Kumar
  • 804
  • 2
  • 11
  • 16
  • 12
    This solution won't work if the elements can repeat. – Vipul Jain Jul 28 '19 at 13:14
  • 7
    I think the idea is cool, you can use multiset for repeating elements. But remember to pay attention when you are erasing by value instead of iterator. – Po-Jen Lai Oct 29 '19 at 03:47
  • 4
    For repeated elements you can simply use a map. Just keep a count of keys and when the count of a key becomes 0, remove the key. Solved this problem using the same idea https://www.spoj.com/problems/ARRAYSUB/ – Yasser Hussain Jul 24 '21 at 15:49
  • 1
    @VipulJain You could always use a multi-set. – Tobias Wilfert Feb 24 '23 at 06:09
14

A neat little trick to handle deletes for a priority_queue STL - use another priority_queue, say, del_pq. Keep inserting all the delete values to this. When you are popping values from the original priority queue, check with top of del_pq and see if we wanted to delete it. If it matches, delete the value from the original priority_queue.

This method implements a way to lazily delete the values in our original priority queue. Can take up twice the memory, but average delete and inserts remain O(logN).

Rishabh Jain
  • 197
  • 2
  • 14
  • I am not sure if the element at the top is always the answer, but using unordered_set (or similar) for the deletion should also work in sublinear time. – Ferazhu Apr 23 '23 at 20:50
5

Pradip and MASh sacrifice the time to realize the remove operation. But if time complexity is important to you, I suggest you to use hash min_heap. A Hash table stores the value-pointer and the pointers point to a min_heap. Which means you can spend O(1) time to find the value in min_heap and O(log(n)) to remove(sift-up or sift down) the element.

Y. Xu
  • 51
  • 1
  • 2
-4

Please note the following approach does the things but not the optimized way to solution. For optimized approach, check other answers.

Let you want to delete the 5th element in the priority_queue<type> Q . Then you can do this like:

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);
}
Shafi
  • 1,850
  • 3
  • 22
  • 44
  • 1
    So what is the complexity of this method ? I do not see the need of using a temporary PQ. why not use a vector `vector tempQ` – kevin Nov 25 '16 at 02:06
  • Good point, here we just need a container. I using a vector instead will improve time complexity. I updated the answer. +1 for your point @kevin – Shafi Nov 25 '16 at 14:27