10

How can I remove the tail element of a priority queue? I am trying to implement beam search using a priority queue and once the priority queue is full, I want to remove the last element(the element with the least priority).

Thanks!

P R
  • 1,293
  • 7
  • 28
  • 58
  • 1
    You can create a new `PriorityQueue` instance and move all the elements from the initial queue to the new one except for the tail. – Luiggi Mendoza Feb 27 '13 at 16:34
  • 1
    http://stackoverflow.com/questions/7878026/is-there-a-priorityqueue-implementation-with-fixed-capacity-and-custom-comparato – NPE Feb 27 '13 at 16:37

8 Answers8

6

No easy way. Copy elements from original to new except the last.

PriorityQueue removelast(PriorityQueue pq)
{

    PriorityQueue pqnew = new PriorityQueue();

    while(pq.size() > 1)
    {
        pqnew.add(pq.poll());
    }

    pq.clear();
    return pqnew;
}

called as

pq = removelast(pq);
Sudarshan
  • 37
  • 7
user93353
  • 13,733
  • 8
  • 60
  • 122
6

You could probably use Guava's MinMaxPriorityQueue to do this. It provides peek, poll, and remove methods for both ends of the queue.

Another option is to write a Queue wrapper that enforces bounding, similar to this answer. You'd need to implement offer, add, and addAll to check capacity. Something like:

public class BoundedQueue<E> implements Serializable, Iterable<E>, Collection<E>, Queue<E> {
    private final Queue<E> queue;
    private int capacity;

    public BoundedQueue(Queue<E> queue, int capacity) {
        this.queue = queue;
        this.capacity = capacity;
    }

    @Override
    public boolean offer(E o) {
        if (queue.size() >= capacity)
            return false;
        return queue.add(o);
    }

    @Override
    public boolean add(E o) throws IllegalStateException {
        if (queue.size() >= capacity)
            throw new IllegalStateException("Queue full"); // same behavior as java.util.ArrayBlockingQueue
        return queue.add(o);
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        boolean changed = false;
        for (E o: c)
            changed |= add(o);
        return changed;
    }

    // All other methods simply delegate to 'queue'
}
Marcus
  • 2,128
  • 20
  • 22
matts
  • 6,738
  • 1
  • 33
  • 50
  • Just curious, Since MinMaxPriorityQueue is marked as Beta (unstable as of Guava v22.0), Not sure if it recommended to use it in production code? – Dravidian Jul 29 '20 at 21:17
5

Use an inverting Comparator and remove from the head. If you need both the head and the tail you are using the wrong data structure.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • It is my understanding that remove of a priority Queue is a O(log(N)) operation. If the remove operation is something that will be done a lot, Wouldn't be more effective to use a TreeSet (assuming uniqness isn't a problem) since that should be O(1) – Nicolas Guillaume Jan 26 '21 at 22:28
2

I think, PR's use case is, that he needs the head, but also want to have a small PQ, so the idea is to remove the tail.

As a PQ is realized as a binary tree mapped to an array, the head is always the first element of the backing array (queue[0]), but the tail is not always at the end of the array, you had to search it.

I think a nice way is to subclass PQ and write the following two methods:

    public class MyPriorityQueue<E> extends PriorityQueue<E>
    {
        // constructors

    public E getTail()
     {
        // queue.length can be bigger than this.size() !!
        Object[] queue = this.toArray();
        E tail = (E)queue[0];
        Comparator<? super E> comparator = this.comparator();
        if (comparator !=null)
           for(int i = 1; i < this.size(); i++)
              if ( comparator.compare(tail, (E)queue[i]) < 0)
                 tail = (E)queue[i];
        else
           for(int j = 1; j < this.size(); j++)
              if ( ((Comparable)tail).compareTo( ((Comparable)queue[j]) ) < 0 )
                 tail = (E)queue[j];
        return tail;
     }  

     public E removeTail()
     {
        E tail = this.getTail();
        this.remove(tail);
        return tail;
     }   
    }
parakmiakos
  • 2,994
  • 9
  • 29
  • 43
max
  • 781
  • 6
  • 3
1

While you add the data into priority queue itself do the comparison;

PriorityQueue<Integer> pq = new PriorityQueue<Integer>(Collections.reverseOrder());
Vanji
  • 1,696
  • 14
  • 23
0

There is a better solution in case you have a reason not to generate another element to the memory.

you can get the size of the Queue and run over it with an iterator while counting the elements you are going over, once you get to the last one OR the one you are looking for, you can use PriorityQueue.remove(Object o)

Iterator<E> it = Queue.iterator();
while (it.hasNext()) {
   temp<E> = it.next();
   counter++;
   if (counter == Queue.size()) {
       Queue.remove(temp);
    }
}
T-G
  • 1
  • 2
0

Removing the tail is not supported.

The best thing you can do is to swap the order of elements so that the tail becomes head, and remove() it instead.

Ilya Gazman
  • 31,250
  • 24
  • 137
  • 216
-3

If you are concerned with the runtime, I suggest implementing your own queue. I did the following and worked in my project.

1) copy paste the code from PriorityQueue -> CustomQueue.java 2) Add a method removeLast() 3) This is the implementation I used (very minimal)

public void removeLast() {
    if(size == 0) {
       return;
    }
    queue[size - 1] = null;
    size--;

}

The reason this works is that the implementation of PriorityQueue uses an array to hold object. So "size" is infact a pointer to the next available spot in the array. By reducing it, the size of the array/queue is reduced, as if you are dropping the last element.

Ehsan
  • 92
  • 5