Priority queue
Concept of priority queue
We know that the queue is a first in first out structure, while the priority queue refers to that the entry and exit order of elements is in order. When leaving the queue, the first out queue of elements with high or low priority may be required. This data structure is called the priority queue; This structure is thread unsafe;
Similar to the "military priority" generally set up in hospitals, banks, airports or other places;
Position in the collection frame:
The arrow represents inheritance relationship!
Use of priority queues
1. Three construction modes
- Create an empty priority queue
//Create an empty priority queue - the default capacity is 11 PriorityQueue q=new PriorityQueue();
-Create a priority queue with an initial capacity of 100
//Set the priority queue with an initial capacity of 100 PriorityQueue q1=new PriorityQueue(100);
- Create priority queues using other containers in the Collection
//Construct with the help of other containers in the collection framework List<Integer> list =new ArrayList<>(); list.add(1); list.add(2); PriorityQueue q2=new PriorityQueue(list);
2. Common methods in priority queue
Boolean offer (E, e): insert element E and return true successfully. When the e object is empty, a NullPointerException exception is thrown;
E peek(): get the element with the highest or lowest priority. When the priority queue is empty, null is returned;
E poll(): deletes the element with the highest or lowest priority. When the priority queue is empty, null is returned;
int size(): get the number of valid elements;
void clear(): clear;
boolean isEmpty(): check whether the priority queue is empty;
The test code is as follows:
public class TestPriorityQueue { public static void method(){ PriorityQueue q=new PriorityQueue(); q.offer(1); q.offer(2); q.offer(5); q.offer(3); q.offer(0); q.offer(4); System.out.println(q.size()); //6 System.out.println(q.peek()); //0 q.poll(); q.poll(); System.out.println(q.size()); //4 System.out.println(q.peek()); //2 q.clear(); if(q.isEmpty()){ System.out.println("Priority queue is empty"); }else{ System.out.println("Priority queue is not empty"); } } public static void main(String[] args) { method(); } }
3. Precautions during use
- When using, you must import the package where PriorityQueue is located;
import java.util.PriorityQueue;
- Cannot insert null object, otherwise null pointer exception (NullPointerException) will be thrown;
q.offer(null);
The following exception will be thrown:
-
The elements placed in the PriorityQueue must be able to compare sizes, and cannot insert objects whose sizes cannot be compared, otherwise ClassCastException will be thrown;
-
There is no capacity limit, and automatic capacity expansion is supported internally;
When the capacity is less than 64, the capacity shall be expanded by twice the oldCapacity;
When the capacity is greater than or equal to 64, expand the capacity by 1.5 times of oldCapacity;
When the capacity exceeds MAX_ARRAY_SIZE, according to MAX_ARRAY_SIZE for capacity expansion;
- The time complexity of inserting and deleting elements is Log2(N);
- The heap data structure is used at the bottom of PriorityQueue;
- PriorityQueue creates a small heap by default - that is, the element obtained each time is the smallest element;
Note:
By default, the system creates a small heap. If you want to create a large heap, you need to construct a Comparator ------ > essence: implement the Comparator interface and rewrite the compare method in the interface;
The code is as follows:
public static void method3() { PriorityQueue<Integer> q = new PriorityQueue<>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2-o1; //o1-o2 is a small pile } }); q.offer(1); q.offer(2); q.offer(5); q.offer(3); System.out.println(q.peek()); //5 }
Simulated implementation of priority queue ----- > heap
The data structure of the heap is adopted at the bottom of the priority queue, as shown below 👇 Make an introduction ~ ~
Concept of heap
In short, heap is a complete binary tree, and for any node: (1) when the node is larger than its child node, it is called heap; (2) When the node is smaller than its child node, it is called a small heap;
Note: the heap with the largest root node is called the maximum heap or large root heap, and the heap with the smallest root node is called the minimum heap or small root heap;
Its storage structure is a continuous space, as shown below:
The storage method is as follows:
Sequential storage of heap
Reasons for sequential storage:
- Heap is a complete binary tree, which can achieve efficient storage and high space utilization;
- The definition subscript of a complete binary tree exactly corresponds to the first subscript of the array, which is 0;
Heap creation
What if the data in the set {27,15,19,18,28,34,65,49,25,37} is created into a heap?
It is not difficult to find that the left and right subtrees of the root node have met the nature of the heap (except for the root node, other nodes are smaller than their child nodes), as shown in the following figure:
Therefore, just adjust the root node * * downward * * to the appropriate position to create a small heap;
Downward adjustment steps:
- a. Let parent mark the node to be adjusted, and child mark the node of its left child;
- b. When the left child exists, that is: (child < size) cycle through the following two steps: (1) when the right child also exists, compare the left and right children and let the child mark the smaller child found; (2) Compare the parent with the younger child, and exchange when the parent > child; However, the exchange may cause the lower subtree to fail to meet the characteristics of the heap. Therefore, it is necessary to update the variable (parent=child, child=parent*2+1) and continue the B operation,
be careful:
When a node is adjusted downward, the left and right subtrees of the node must meet the characteristics of heap before it can be used;
If you don't understand, just look at the picture:
However, the obvious subtree does not meet the characteristics of the heap, so the variables need to be updated;
The specific codes are as follows:
public void shiftDown(int[] array){ int parent=0; int child=2*parent+1; //Child to mark the left child int size=array.length; while(child<size){ // When the right child exists, find the smaller one of the left and right children and mark it with child if(child+1 < size && array[child+1] < array[child]){ child += 1; } // If the parents are younger than their youngest child, it indicates that the structure has met the characteristics of heap if (array[parent] <= array[child]) { break; }else{ //Exchange parents with younger children int temp=array[parent]; array[parent]=array[child]; array[child]=temp; // When the large element in the parent moves down, the subtree may not meet the nature of the heap, so it needs to continue to adjust downward parent = child; child = parent * 2 + 1; } } } public static void main(String[] args) { int[] array={27,15,19,18,28,34,65,49,25,37}; shiftDown(array); } }
Time complexity analysis:
From root to leaf, the number of comparisons is the height of the complete binary tree, that is, the time complexity is O(log2(N));
For the special case in Figure 1 above, you can directly adjust its root node. However, if the sequence is {65,37,34,49,28,19,27,18,25,15}, how to create it? As shown in Figure 2 below:
Idea (arbitrary sequence heap building):
- (1) Find the penultimate non leaf node in the current tree, which is also the location of the parents of the last node, and the location subscript of the last node is size-1;
The subscript of its parents is: ((size-1) - 1) / 2; - (2) From the non leaf node to the root position, if a node is encountered, the node is used as a binary tree for downward adjustment;
The code is as follows:
public static void createHeap(int[] array) { // Find the penultimate non leaf node, starting from the position of the node to the root node. If you encounter a node, apply downward adjustment int root = ((array.length-2)/2); for (; root >= 0; root--) { shiftDown(array,root); } }
Implementing priority queues using heap emulation
- Heap insertion
(1) Put elements into the underlying space;
(2) Adjust the last newly inserted node upward until it meets the characteristics of the heap;
public void offer(int e){ array[size]=e; size++; //Adjust the inserted new element upward shiftUp(array,size-1); }
- Deletion of heap
(1) Exchange the top element of the heap with the last element in the heap;
(2) Reduce the number of valid data in the heap by one;
(3) Adjust the top element downward;
public Integer poll(){ int ret=array[0]; //Swap the top element with the last element array[0]=array[size-1]; //The number of effective elements in the heap is reduced by one size--; //Adjust the heap top element down to the appropriate position using shiftDown(array,size,0); return ret; }
- Get heap top element
public int peek(){ return array[0]; }
Application of heap
class Solution { public int[] getLeastNumbers(int[] arr, int k) { // Carry out parameter detection if( arr==null || k <= 0){ return new int[0]; } PriorityQueue<Integer> q = new PriorityQueue<>(); // Put the elements in the array into the heap in turn for(int i = 0; i < arr.length; ++i){ q.offer(arr[i]); } // Put the first k elements of the priority queue into the array int[] ret = new int[k]; for(int i = 0; i < k; ++i){ ret[i] = q.poll(); } return ret; } }