The difference between the priority queue and the queue is that the first element out of the priority queue is always the element with the highest priority
Java provides the PriorityQueue class to implement the priority Queue. Because it implements the Queue interface, it can also be referenced through the Queue
Queue<Integer> priorityQueue = new PriorityQueue<>((a,b)->b-a);
Unlike Queue, when defining PriorityQueue, you need to pass in a Comparator, which will determine the priority of elements. The decision method is similar to the sort() method of List, that is, when passing in a and b, if a has higher priority, it will return a negative number, if b has higher priority, it will return a positive number, and if b is equal, it will return 0. The above example shows that higher values have higher priority
You can also not pass in Comparator, so its elements need to implement the Comparable interface, such as
Queue<Integer> priorityQueue = new PriorityQueue<>();
Integer also implements the Comparable interface. The comparison method is as follows
public static int compare(int x, int y) { return (x < y) ? -1 : ((x == y) ? 0 : 1); }
Therefore, a small value in the priorityQueue above has a higher priority
example:
Queue<Integer> priorityQueue = new PriorityQueue<>(); for(int i=0;i<10;i++){ priorityQueue.add((int)(Math.random()*40+1)); } System.out.println(String.format("list:%s", priorityQueue)); // list:[6, 8, 19, 18, 15, 35, 27, 40, 30, 36] System.out.println(priorityQueue.poll()); // 6 System.out.println(priorityQueue.poll()); // 8 System.out.println(priorityQueue.poll()); // 15
Implementation principle
The implementation effect of priority queue is like sorting it, but in fact it is a heap. The heap is a complete binary tree. Taking the maximum heap as an example, the value of its parent node is always greater than that of its child node. In priority queue, the priority of the parent node is always greater than that of the child node. When fetching elements, always take the value of the root node, and then adjust the heap to conform to the nature of the heap, so that the elements with the highest priority are fetched every time
Heap has extremely high efficiency. Adding elements or taking out a root node element and maintaining the nature of heap only requires the time complexity of log2n, which is the height of binary tree
In PriorityQueue, an array Object[] queue is used to store elements because we can obtain the values of parent and child nodes through calculation:
- Parent node: (i-1)/2
- Left child node: i*2+1
- Right child node: i*2+2
offer()
When adding elements to the PriorityQueue, if the queue is empty, it will be added directly. If it is not empty, it will be added to the end of the queue first, and then the heap will be adjusted
private void siftUpComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super E>) x; while (k > 0) { int parent = (k - 1) >>> 1; Object e = queue[parent]; if (key.compareTo((E) e) >= 0) break; queue[k] = e; k = parent; } queue[k] = key; }
The above is the process of adjusting the heap for PriorityQueue:
- Gets and compares the parent node
- Ends if the priority is less than the parent node
- If the priority is higher than the parent node, exchange with the parent node, and repeat the above operation up to the root node
poll()
public E poll() { if (size == 0) return null; int s = --size; modCount++; E result = (E) queue[0]; E x = (E) queue[s]; queue[s] = null; if (s != 0) siftDown(0, x); return result; }
When taking the element, get the element at the head of the team, put the element at the end of the team into the head of the team, and adjust the heap downward from the top of the heap through siftDown(0, x)
private void siftDownComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super E>)x; int half = size >>> 1; // loop while a non-leaf while (k < half) { int child = (k << 1) + 1; // assume left child is least Object c = queue[child]; int right = child + 1; if (right < size && ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0) c = queue[child = right]; if (key.compareTo((E) c) <= 0) break; queue[k] = c; k = child; } queue[k] = key; }
The adjustment method is as follows:
- Get the value of left and right nodes
- Select the larger of the two nodes
- Compared with the selected child node, if the priority is lower than the child node, it will be exchanged with it, and the above operation will be repeated, otherwise it will end
It is worth mentioning that the range of K is limited to < half, so that valid left child nodes can always be obtained in the loop. At the same time, it also ensures that the last left child node can be accessed, that is, when k=half-1, child=k*2+1=half*2-2=size-1, that is, the last node
reference resources
Use PriorityQueue - Liao Xuefeng's official website (liaoxuefeng.com)