注:此博文为本人学习过程中的笔记
1.优先级队列
1.1.概念
前面学过队列,队列是一种先进先出的数据结构,当有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列,该场景下,使用队列显然不合适。
在这种情况下,数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。这种数据结构就是优先级队列。
2.优先级队列的模拟实现
JDK1.8中的PriorityQueue底层使用了堆这种数据结构,而堆实际就是在完全二叉树的基础上进行了一些调整。
2.1.堆的概念
如果有一个关键码的集合,把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,且满足二叉树中的根节点均大于子节点或均小于子节点,则称为堆。根节点均大于子节点的称为大根堆或最大堆,根节点均小于子节点的称为小根堆或最小堆。
堆的性质:
堆中的某个节点的值总是不大于或不小于其父节点的值
堆总是一棵完全二叉树
2.2.堆的存储方式
从堆的概念可知,堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储
注意:对于非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低。
将元素存储到数组后,假设i为节点在数组中的下标,则有:
如果i为0,则i表示的节点为根节点,否则i节点的双亲记得为(i - 1) / 2
如果2 * i + 1小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
如果2 * i + 2小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子
3. PriorityQueue
3.1.PriorityQueue的特性
Java集合框架中提供了PriorityQueue和PriorituBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的。
使用PriorityQueue时要注意:
1.使用时必须导入PriorityQueue所在的包
2.PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出异常
3.不能插入null对象,否则会抛出空指针异常
4.没有容量限制,可以插入多个元素,其内部可以自动扩容
5.PriorityQueue底层使用了堆数据结构
6.PriorityQueue默认情况下是小根堆,即每次获取到的元素都是最小的元素
3.2.PriorityQueue的常用接口介绍
3.2.1.优先级队列常见的构造方法
PriorityQueue() 创建一个空的优先级队列,默认容量是11
PriorityQueue(int initialCapacity) 创建一个初始容量为initialCapacity的优先级队列,注意,initialCapacity不能小于1,否则会抛出异常
PriorityQueue(Collection<? extends E> c) 用一个集合来创建优先级队列
注意:默认情况下,PriorityQueue队列是小根堆,如果需要大根堆用户提供比较器
//用户自己定义的比较器,直接实现Comparator接口,然后重写该接口中的compare方法即可
class IntCmp implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}
3.2.2.常用的方法
boolean offer(E e) 插入元素e,插入成功后返回true,如果e对象为空,抛出空指针异常,注意:空间不够是会进行扩容
E peek() 获取优先级最高的元素,如果优先级队列为空,返回null
E poll() 移除优先级最高的元素并返回,如果优先级队列为空,返回null
int size() 获取有效元素的个数
void clear() 清空
boolean isEmpty() 检测优先级队列是否为空,空返回true
4.堆的应用
4.1.PriorityQueue的实现
用堆作为底层结构封装优先级队列
4.2.堆排序
堆排序即利用堆的思想来进行排序,总共分为两个步骤:
1.建堆
升序:建大根堆
降序:建小根堆
2.利用堆删除思想进行排序