Java 中的优先级队列,是使用数组表示的完全二叉树的小顶堆模式 .
每次取出的元素都是队列中权值最小的,队列中的元素都经过了排序处理,默认按照自然顺序,也可以通过 Comparator 接口进行自定义排序。
How to use PriorityQueue 继承AbstractQueue ,因此可以是用Queue 接口定义的方法来使用。
Queue 接口方法
说明
add(E e)
向队尾插入元素,失败则抛异常
offer(E e)
向队尾插入元素,成功则返回 true
remove()
获取并删除队首元素,失败则跑出异常
poll()
获取并删除队首元素,失败则返回 null
element()
获取队首元素,失败则抛出异常
peek()
获取队首元素,失败则返回 null
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package Container;import java.util.Comparator;import java.util.PriorityQueue;import java.util.Queue;import java.util.Random;public class PriorityQueueTest { public static void main (String[] args) { Queue<Integer> priorityQueue = new PriorityQueue (new Comparator <Integer>() { @Override public int compare (Integer o1, Integer o2) { return o2.compareTo(o1); } }); int count = 0 ; System.out.print("Add value into priority queue: " ); while (count++!=20 ){ Random random = new Random (); int tmp = random.nextInt(100 ); System.out.print(tmp + " " ); priorityQueue.add(tmp); } System.out.println("" ); System.out.print("Pop elements in the queue: " ); while (true ){ Integer tmp = (Integer) priorityQueue.poll(); if (tmp==null ) break ; System.out.print(tmp+" " ); } } } Add value into priority queue: 94 70 83 94 50 28 31 29 70 67 60 97 36 5 65 52 69 48 14 75 小顶堆: 5 14 28 29 31 36 48 50 52 60 65 67 69 70 70 75 83 94 94 97 大顶堆:93 85 83 83 83 80 66 56 42 40 35 34 33 33 30 22 18 12 4 3
PriorityQueue 原理
Priority Queue是基于数组实现的完全二叉树 ,因此可以从当前节点推出左右子节点和父节点的下标位置。
源码解析 数据结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class PriorityQueue <E> extends AbstractQueue <E> implements java .io.Serializable { private static final int DEFAULT_INITIAL_CAPACITY = 11 ; transient Object[] queue; private int size = 0 ; private final Comparator<? super E> comparator; ...... }
Add/Offer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 public boolean add (E e) { if (offer(e)) return true ; else throw new IllegalStateException ("Queue full" ); } public boolean offer (E e) { if (e == null ) throw new NullPointerException (); modCount++; int i = size; if (i >= queue.length) grow(i + 1 ); size = i + 1 ; if (i == 0 ) queue[0 ] = e; else siftUp(i, e); return true ; } private void grow (int minCapacity) { int oldCapacity = queue.length; int newCapacity = oldCapacity + ((oldCapacity < 64 ) ? (oldCapacity + 2 ) : (oldCapacity >> 1 )); if (newCapacity - MAX_ARRAY_SIZE > 0 ) newCapacity = hugeCapacity(minCapacity); queue = Arrays.copyOf(queue, newCapacity); } private void siftUp (int k, E x) { if (comparator != null ) siftUpUsingComparator(k, x); else siftUpComparable(k, x); } 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; } private void siftUpUsingComparator (int k, E x) { while (k > 0 ) { int parent = (k - 1 ) >>> 1 ; Object e = queue[parent]; if (comparator.compare(x, (E) e) >= 0 ) break ; queue[k] = e; k = parent; } queue[k] = x; }
插入新数据后,会破坏小顶堆的性质,通过将插入的元素与父节点比较交换,直到满足父节点的值都小于子节点的值。
Poll & Remove
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 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; } private void siftDown (int k, E x) { if (comparator != null ) siftDownUsingComparator(k, x); else siftDownComparable(k, x); } private void siftDownComparable (int k, E x) { Comparable<? super E> key = (Comparable<? super E>)x; int half = size >>> 1 ; while (k < half) { int child = (k << 1 ) + 1 ; 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; } private void siftDownUsingComparator (int k, E x) { int half = size >>> 1 ; while (k < half) { int child = (k << 1 ) + 1 ; Object c = queue[child]; int right = child + 1 ; if (right < size && comparator.compare((E) c, (E) queue[right]) > 0 ) c = queue[child = right]; if (comparator.compare(x, (E) c) <= 0 ) break ; queue[k] = c; k = child; } queue[k] = x; }
默认的删除调整中,首先获取顶部下标和最尾部的元素内容,从顶部的位置开始,将尾部元素的内容逐层向下与当前点的左右子节点中较小的那个交换,直到判断元素内容小于或等于左右子节点中的任何一个为止。
element && peek 1 2 3 4 5 6 7 8 9 10 11 12 public E peek () { return (size == 0 ) ? null : (E) queue[0 ]; } public E element () { E x = peek(); if (x != null ) return x; else throw new NoSuchElementException (); }
Reference 你应该知道的 PriorityQueue ——深入浅出分析 PriorityQueue