I'm just studying for exams right now, and came across this question in the sample exam:
Block Implementation of a Priority Queue
If we know in advance that a priority queue will only ever need to cater for a small number of discrete priorities (say 10), we can implement all operations of the priority queue in constant time by representing the priority queue as an array of queues - each queue storing the elements of a single priority. Note that while an operation may be linear in the number of priorities in the priority queue, the operation is still constant with respect to the size of the overall data structure.
The Objects stored in this priority queue is not comparable.
I have attempted it but I am lost as to how I am supposed to assign priority with a array implementation priority queue.
I have also tried looking for solutions, but all I've managed to find are examples that used Comparable, which we did not learn in this course.
Question: http://imgur.com/3mlBoW7
Each of the arrays will correspond to a different priority. Your lowest level priority array will deal only with objects of that priority level. Your highest level priority array will deal with objects of highest priority level, and so on. When you receive a new object, you place it into the array that corresponds to its priority.
It doesn't matter, then, that objects are not comparable since they are sorted by priority based on the stratification of the arrays. Then, when you are looking for next elements to execute, you check the highest priority array and see if there are any elements; if not, move to the next priority, and so on through each array.
I'm hoping I understood the problem and your question correctly; let me know if you have any additional questions in regards to my answer.
Following on Imcphers' answer, this would be a simple implementation in Java. Note that you do not need Comparable because enqueue takes an extra parameter, namely the discrete priority of the newly-added element:
public class PQueue<T> {
public static final int MAX_PRIORITIES = 10;
private ArrayList<ArrayDeque<T> > queues = new ArrayList<>();
public PQueue() {
for (int i=0; i<MAX_PRIORITIES; i++) {
queues.add(new ArrayDeque<T>());
}
}
public void enqueue(int priority, T element) {
// ... add element to the end of queues[priority]
}
public T dequeue() {
// ... find first non-empty queue and pop&return its first element
}
// ... other methods
}
Here, enqueue() and dequeue() are both O(1), because you know in advance how many priorities there can be, and what their values are (0 to MAX_PRIORITIES-1) so that no sorting is required, and search of a non-empty queue is constant-time (at most, MAX_PRIORITIES queues will have to be tested for emptyness). If these parameters are not known, the best implementation would use
private TreeSet<ArrayDeque<T extends Comparable> > queues
= new TreeSet<>(CustomComparator);
Where the CustomComparator asks queues to sort themselves depending on the natural order of their first elements, and which needs to keep these internal queues sorted after each call to enqueue --- this ups the complexity of enqueue/dequeue to O(log p), where p is the number of distinct priorities (and therefore, internal queues).
Note that Java's PriorityQueue belongs to the same complexity class, but avoids all that object overhead contributed by the TreeSet / ArrayDeque wrappers by implementing its own internal priorty heap.
Related
I define a maximum priority queue as below:
PriorityQueue<Integer> queue = new PriorityQueue<>(Collections.reverseOrder());
queue.add(25);
queue.add(3);
queue.add(1);
queue.add(3);
queue.add(4);
I need to understand how this works especially when does the 1 gets the index of 2 only (not 4)?
Thanks.
It has no guaranteed order (https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/PriorityQueue.html):
The Iterator provided in method iterator() and the Spliterator provided in method spliterator() are not guaranteed to traverse the elements of the priority queue in any particular order. If you need ordered traversal, consider using Arrays.sort(pq.toArray()).
Normally you retrieve elements according to their natural order using the poll() method, for example:
while (!queue.isEmpty()) {
var element = queue.poll();
...
}
EDIT:
If you want to look at the internals of the class, this part of the code may be relevant (it basically uses a heap data structure):
/**
* Priority queue represented as a balanced binary heap: the two
* children of queue[n] are queue[2*n+1] and queue[2*(n+1)]. The
* priority queue is ordered by comparator, or by the elements'
* natural ordering, if comparator is null: For each node n in the
* heap and each descendant d of n, n <= d. The element with the
* lowest value is in queue[0], assuming the queue is nonempty.
*/
transient Object[] queue;
This class use structure called Heap, and store it in array.
You will receive objects in proper order when you poll them.
I'm trying to use a PriorityQueue to order objects using a Comparator.
This can be achieved easily, but the objects class variables (with which the comparator calculates priority) may change after the initial insertion. Most people have suggested the simple solution of removing the object, updating the values and reinserting it again, as this is when the priority queue's comparator is put into action.
Is there a better way other than just creating a wrapper class around the PriorityQueue to do this?
You have to remove and re-insert, as the queue works by putting new elements in the appropriate position when they are inserted. This is much faster than the alternative of finding the highest-priority element every time you pull out of the queue. The drawback is that you cannot change the priority after the element has been inserted. A TreeMap has the same limitation (as does a HashMap, which also breaks when the hashcode of its elements changes after insertion).
If you want to write a wrapper, you can move the comparison code from enqueue to dequeue. You would not need to sort at enqueue time anymore (because the order it creates would not be reliable anyway if you allow changes).
But this will perform worse, and you want to synchronize on the queue if you change any of the priorities. Since you need to add synchronization code when updating priorities, you might as well just dequeue and enqueue (you need the reference to the queue in both cases).
I don't know if there is a Java implementation, but if you're changing key values alot, you can use a Fibonnaci heap, which has O(1) amortized cost to decrease a key value of an entry in the heap, rather than O(log(n)) as in an ordinary heap.
One easy solution that you can implement is by just adding that element again into the priority queue. It will not change the way you extract the elements although it will consume more space but that also won't be too much to effect your running time.
To proof this let's consider dijkstra algorithm below
public int[] dijkstra() {
int distance[] = new int[this.vertices];
int previous[] = new int[this.vertices];
for (int i = 0; i < this.vertices; i++) {
distance[i] = Integer.MAX_VALUE;
previous[i] = -1;
}
distance[0] = 0;
previous[0] = 0;
PriorityQueue<Node> pQueue = new PriorityQueue<>(this.vertices, new NodeComparison());
addValues(pQueue, distance);
while (!pQueue.isEmpty()) {
Node n = pQueue.remove();
List<Edge> neighbours = adjacencyList.get(n.position);
for (Edge neighbour : neighbours) {
if (distance[neighbour.destination] > distance[n.position] + neighbour.weight) {
distance[neighbour.destination] = distance[n.position] + neighbour.weight;
previous[neighbour.destination] = n.position;
pQueue.add(new Node(neighbour.destination, distance[neighbour.destination]));
}
}
}
return previous;
}
Here our interest is in line
pQueue.add(new Node(neighbour.destination, distance[neighbour.destination]));
I am not changing priority of the particular node by removing it and adding again rather I am just adding new node with same value but different priority.
Now at the time of extracting I will always get this node first because I have implemented min heap here and the node with value greater than this (less priority) always be extracted afterwards and in this way all neighboring nodes will already be relaxed when less prior element will be extracted.
Without reimplementing the priority queue yourself (so by only using utils.PriorityQueue) you have essentially two main approaches:
1) Remove and put back
Remove element then put it back with new priority. This is explained in the answers above. Removing an element is O(n) so this approach is quite slow.
2) Use a Map and keep stale items in the queue
Keep a HashMap of item -> priority. The keys of the map are the items (without their priority) and the values of the map are the priorities.
Keep it in sync with the PriorityQueue (i.e. every time you add or remove an item from the Queue, update the Map accordingly).
Now when you need to change the priority of an item, simply add the same item to the queue with a different priority (and update the map of course). When you poll an item from the queue, check if its priority is the same than in your map. If not, then ditch it and poll again.
If you don't need to change the priorities too often, this second approach is faster. Your heap will be larger and you might need to poll more times, but you don't need to find your item.
The 'change priority' operation would be O(f(n)log n*), with f(n) the number of 'change priority' operation per item and n* the actual size of your heap (which is n*f(n)).
I believe that if f(n) is O(n/logn)(for example f(n) = O(sqrt(n)), this is faster than the first approach.
Note : in the explanation above, by priority I means all the variables that are used in your Comparator. Also your item need to implement equals and hashcode, and both methods shouldn't use the priority variables.
It depends a lot on whether you have direct control of when the values change.
If you know when the values change, you can either remove and reinsert (which in fact is fairly expensive, as removing requires a linear scan over the heap!).
Furthermore, you can use an UpdatableHeap structure (not in stock java though) for this situation. Essentially, that is a heap that tracks the position of elements in a hashmap. This way, when the priority of an element changes, it can repair the heap. Third, you can look for an Fibonacci heap which does the same.
Depending on your update rate, a linear scan / quicksort / QuickSelect each time might also work. In particular if you have much more updates than pulls, this is the way to go. QuickSelect is probably best if you have batches of update and then batches of pull opertions.
To trigger reheapify try this:
if(!priorityQueue.isEmpty()) {
priorityQueue.add(priorityQueue.remove());
}
Something I've tried and it works so far, is peeking to see if the reference to the object you're changing is the same as the head of the PriorityQueue, if it is, then you poll(), change then re-insert; else you can change without polling because when the head is polled, then the heap is heapified anyways.
DOWNSIDE: This changes the priority for Objects with the same Priority.
Is there a better way other than just creating a wrapper class around the PriorityQueue to do this?
It depends on the definition of "better" and the implementation of the wrapper.
If the implementation of the wrapper is to re-insert the value using the PriorityQueue's .remove(...) and .add(...) methods,
it's important to point out that .remove(...) runs in O(n) time.
Depending on the heap implementation,
updating the priority of a value can be done in O(log n) or even O(1) time,
therefore this wrapper suggestion may fall short of common expectations.
If you want to minimize your effort to implement,
as well as the risk of bugs of any custom solution,
then a wrapper that performs re-insert looks easy and safe.
If you want the implementation to be faster than O(n),
then you have some options:
Implement a heap yourself. The wikipedia entry describes multiple variants with their properties. This approach is likely to get your the best performance, at the same time the more code you write yourself, the greater the risk of bugs.
Implement a different kind of wrapper: handlee updating the priority by marking the entry as removed, and add a new entry with the revised priority.
This is relatively easy to do (less code), see below, though it has its own caveats.
I came across the second idea in Python's documentation,
and applied it to implement a reusable data structure in Java (see caveats at the bottom):
public class UpdatableHeap<T> {
private final PriorityQueue<Node<T>> pq = new PriorityQueue<>(Comparator.comparingInt(node -> node.priority));
private final Map<T, Node<T>> entries = new HashMap<>();
public void addOrUpdate(T value, int priority) {
if (entries.containsKey(value)) {
entries.remove(value).removed = true;
}
Node<T> node = new Node<>(value, priority);
entries.put(value, node);
pq.add(node);
}
public T pop() {
while (!pq.isEmpty()) {
Node<T> node = pq.poll();
if (!node.removed) {
entries.remove(node.value);
return node.value;
}
}
throw new IllegalStateException("pop from empty heap");
}
public boolean isEmpty() {
return entries.isEmpty();
}
private static class Node<T> {
private final T value;
private final int priority;
private boolean removed = false;
private Node(T value, int priority) {
this.value = value;
this.priority = priority;
}
}
}
Note some caveats:
Entries marked removed stay in memory until they are popped
This can be unacceptable in use cases with very frequent updates
The internal Node wrapped around the actual values is an extra memory overhead (constant per entry). There is also an internal Map, mapping all the values currently in the priority queue to their Node wrapper.
Since the values are used in a map, users must be aware of the usual cautions when using a map, and make sure to have appropriate equals and hashCode implementations.
I have a question regarding implementing priority queue for Dijkstra's algorithm, because this is a hw so I couldn't create another class, so I'm trying to find a way to add nodes(integer)in priority queue but I want the queue to sort the weight inside the node, but not the node itself.
For example,I have 3 nodes(0,1,2) and node 0 has a weight of 10, node 1 has 15, and node 2 has 5.
Queue<Integer> queue = new PriorityQueue<Integer>();
queue.add(0);
queue.add(1);
queue.add(2);
while(!queue.isEmpty()){
System.out.println(queue.poll());
}
This should give me a output of 2,0,1.
Is this possible without creating another class? Or is there a different approach I could use besides priority queue?
Thanks in advance!!!!!! any help would be much appreciated!
One solution I could think of is sorting a normal queue every time I add a node into it, so if I have node 2,0,1 in the queue and I want to add node 3 which has a weight of 8, I would need to compare the weight with the top element of queue until it fits into the queue, so it would be 2,3,0,1 in queue, but this is kindda inefficient tho.
You have two options:
Create your own Node class, and make it implement the Comparable<Node> interface. The class will have a weight attribute, and it could compare Nodes based on their weight. This will create a natural ordering of Nodes that PriorityQueue will use.
Create the priority queue with the PriorityQueue(Comparator<? super E> comparator) constructor. It takes a comparison function (Comparator) for comparing two items. You can therefore have that function compare using the node's weights (which don't have to be kept in the same queue - the might be calculated dynamically or be kept in some separate data structure).
I'm reading about PriorityQueues in javadocs and it mentions the term tie-breaking. I couldn't undersand what does the term mean. I hope someone could explain.
In Java, comparison is done using a compare(a,b) method (for comparators) or a.compareTo(b) method (for class instances that can be compared). This method is supposed to return a negative number whenever a < b, a positive number when a > b, and 0 when a = b.
However sometimes people just use return value 0 to mean a and b are incomparable (some orderings aren't total). In this case, the PriorityQueue has to decide which element goes first. This is tie-breaking. Specifically some priority queues preserve the order in which zero-comparing elements were inserted, so in that case insertion time is the tie-breaker. Then for a collection of elements where compareTo() always returns 0, the priority queue would act just like a normal queue.
If you have a priority Que that uses Node.Score to assign priority, if two scores are the same you have a tie.
You could implement first-in-first-out tie-breaking to comparable elements. If two scores are the same the priority is given to the node that was added first.
if(NodeA.getScore() == NodeB.getScore()){
//this is a tie
if(NodeA.getOrderAdded() > NodeB.GetOrderAdded(){
//NodeA has priority
} else {
//NodeB has priority
}
}
I thought add() is supposed to ignore duplicates, but my output has duplicates. How do I not store duplicates?
I would also like to know how the priority queue checks if two elements are duplicates. I'm guessing it's using the comparator equals, but I just want to be sure.
Thanks
Here is a part from PriorityQueue Javadoc:
This queue orders elements according to an order specified at construction time, which is specified either according to their natural order (see Comparable), or according to a Comparator, depending on which constructor is used.
So yes, PriorityQueue uses Comparator (if you specified it as a constructor argument) or uses compareTo(...) method (elements must implement Comparable interface).
PriorityQueue allows duplicates. So if you want to avoid that, you need to implement your own version of Queue. You can find very elegant way, how to do that in "Effective Java", page 85. Alternatively you might extend PriorityQueue class and override add method (this is a perfect place to put contains(...) check).
A PriorityQueue in Java does not have any restriction with regard to duplicate elements. If you want to ensure that two identical items are never present in the priority queue at the same time the simplest way would be to maintain a separate Set in parallel with the priority queue. Each time you want to insert an element into the priority queue you can check if the set contains it already, if not, then add it to both the set and the priority queue. Whenever you remove an element from the priority queue then just remove that element from the set as well.
Alternatively, depending on which operations you intend to perform on the priority queue, and how equality is defined in your case, it might be viable to replace it with a single TreeSet instead since that will still allow you to perform all important operations that you would have access to in a priority queue while it additionally does not allow duplicates.
The following sample implementation
import java.util.PriorityQueue;
public class NoDuplicates<E> extends PriorityQueue<E>
{
#Override
public boolean offer(E e)
{
boolean isAdded = false;
if(!super.contains(e))
{
isAdded = super.offer(e);
}
return isAdded;
}
public static void main(String args[])
{
PriorityQueue<Integer> p = new NoDuplicates<Integer>();
p.add(10);
p.add(20);
p.add(10);
for(int i =0;i<=2;i++)
{
System.out.println(p.poll());
}
}
}
results in
10
20
null
which shows that it doesn't add a duplicate element 10.
Sets are the only things which ignore duplicates. Lists and queues do not. (LinkedList is a Queue)
If you want to drop duplicates you can check whether the entry you take() is the same as the previous and ignore it. You can do the comparison any way you like. ;)