目录
优先级队列(PriorityQueue)
概念
首先,队列是一种先进先出
(FIFO)
的数据结构
,但有些情况下,
操作的数据可能带有优先级,一般出队
列时,可能需要优先级高的元素先出队列
,该中场景下,使用队列显然不合适,比如:在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话。 在这种情况下,我们的数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象
。这种数据结构就是优先级队列
(Priority Queue)
。
优先级队列的特性
Java
集合框架中提供了
PriorityQueue
和
PriorityBlockingQueue
两种类型的优先级队列,
PriorityQueue
是线
程不安全的,
PriorityBlockingQueue
是线程安全的
使用优先级队列时需注意:
1.使用时必须导入PriorityQueue所在的包,即:
import java.util.PriorityQueue;
2. PriorityQueue
中放置的
元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出
ClassCastException
异常
3.
不能
插入
null
对象,否则会抛出
NullPointerException
4.
没有容量限制,可以插入任意多个元素,其内部可以自动扩容
5. 插入和删除元素的时间复杂度为O(log₂N)
6.
PriorityQueue
底层使用了堆数据结构
7.
PriorityQueue
默认情况下是小堆
---
即每次获取到的元素都是最小的元素
优先级队列的构造
此处只是列出了PriorityQueue中常见的几种构造方式
构造器 | 功能介绍 |
PriorityQueue()
|
创建一个空的优先级队列,默认容量是11
|
PriorityQueue(int
initialCapacity)
|
创建一个初始容量为
initialCapacity
的优先级队列,注意:initialCapacity不能小于
1
,否则会抛Ill
egalArgumentException
异常
|
PriorityQueue(Collection<?
extends E> c)
|
用一个集合来创建优先级队列
|
static void TestPriorityQueue(){
// 创建一个空的优先级队列,底层默认容量是11
PriorityQueue<Integer> p1 = new PriorityQueue<>();
// 创建一个空的优先级队列,底层的容量为initialCapacity
PriorityQueue<Integer> p2 = new PriorityQueue<>(100);
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(3);
list.add(2);
list.add(1);
// 用ArrayList对象来构造一个优先级队列的对象
PriorityQueue<Integer> p3 = new PriorityQueue<>(list);
System.out.println(p3.size());//size = 4
System.out.println(p3.peek());//peek = 1
}
注意:默认情况下,
PriorityQueue
队列是小堆,如果需要大堆需要用户提供比较器
// 用户自己定义的比较器:直接实现Comparator接口,然后重写该接口中的compare方法即可
class IntCmp implements Comparator<Integer>{
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
}
public class TestPriorityQueue {
public static void main(String[] args) {
PriorityQueue<Integer> p = new PriorityQueue<>(new IntCmp());
p.offer(4);
p.offer(3);
p.offer(2);
p.offer(1);
p.offer(5);
System.out.println(p.peek());//peek = 5
}
}
此时创建出来的就是大堆。
优先级队列的操作实现
优先级队列(priority queue)是0个或多个元素的集合,每个元素都有一个优先权,对优先级队列执行的操作有(1)查找(2)插入一个新元素(3)删除 一般情况下,查找操作用来搜索优先权最大的元素,删除操作用来删除该元素 。对于优先权相同的元素,可按先进先出次序处理或按任意优先权进行。
函数名 | 功能介绍 |
boolean offer(E e)
|
插入元素
e
,插入成功返回
true
,如果
e
对象为空,抛出
NullPointerException
异常,时间复杂度O(log₂N),注意:空间不够时候会进行扩容
|
E peek()
|
获取优先级最高的元素,如果优先级队列为空,返回
null
|
E poll()
|
移除优先级最高的元素并返回,如果优先级队列为空,返回
null
|
int size()
|
获取有效元素的个数
|
void clear()
|
清空
|
boolean isEmpty()
|
检测优先级队列是否为空,如果空返回
true
|
static void TestPriorityQueue2(){
int[] arr = {4,1,9,2,8,0,7,3,6,5};
PriorityQueue<Integer> q = new PriorityQueue<>(arr.length);
for (int i = 0; i < arr.length; i++) {
q.offer(i);
}
System.out.println(q.size()); // 打印优先级队列中有效元素个数
System.out.println(q.peek()); // 获取优先级最高的元素
// 从优先级队列中删除两个元素,再次获取优先级最高的元素
q.poll();
q.poll();
System.out.println(q.size()); // 打印优先级队列中有效元素个数
System.out.println(q.peek()); // 获取优先级最高的元素
q.offer(0);
System.out.println(q.peek()); // 获取优先级最高的元素
// 将优先级队列中的有效元素删除掉,检测其是否为空
q.clear();
if(q.isEmpty()){
System.out.println("优先级队列已经为空");
}else{
System.out.println("优先级队列不为空");
}
}
以下是
JDK 1.8中,
优先级队列的扩容说明:
- 如果容量小于64时,是按照oldCapacity的2倍方式扩容的
- 如果容量大于等于64时,是按照oldCapacity的1.5倍方式扩容的
- 如果容量超过MAX_ARRAY_SIZE,按照MAX_ARRAY_SIZE来进行扩容
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
int oldCapacity = queue.length;
// Double size if small; else grow by 50%
int newCapacity = oldCapacity + ((oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1));
// overflow-conscious code
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
queue = Arrays.copyOf(queue, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}