优先级队列
概念:
数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。这种数据结构就是优先级队列(Priority Queue)。
模拟实现:
PriorityQueue底层使用了堆这种数据结构
,而堆实际就是在完全二叉树的基础上进行 了一些调整。
堆
概念:
如果有一个关键码的集合K = {k0
,
k1
,
k2
,
…
,
kn-1}
,把它的所有元素按完全二叉树的顺序存储方式存储 在一
个一维数组中,并满足:
Ki <= K2i+1
且
Ki<= K2i+2
(Ki >= K2i+1
且
Ki >= K2i+2) i = 0
,
1
,
2…
,则
称为 小堆
(或大
堆
)
。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
性质:
(1)堆中某个节点的值总是不大于或不小于其父节点的值;
(2)堆总是一棵完全二叉树。
存储方式:
堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储。
注意:对于非完全二叉树,则不适合使用顺序方式进行存储
,因为为了能够还原二叉树,
空间中必须要存储空节点
,就会导致空间利用率比较低。
创建:
默认情况下是小根堆,即每次获得的元素是最小元素
public void createHeap(int[] array) {
for(int i = 0;i < array.length;i++){
elem[i] = array[i];
usedSize++;
}
for (int parent = (usedSize-1-1)/2; parent >= 0 ; parent--) {
shiftDown(parent,usedSize);
}
}
向下调整:
private void shiftDown(int root,int len) {
int child = 2 * root + 1;
while (child < len){
//一定不会越界
if (child + 1 < len && elem[child] < elem[child + 1]){
child = child + 1;
}
if (elem[child] > elem[root]){
int tmp = elem[child];
elem[child] = elem[root];
elem[root] = tmp;
root = child;
child = 2 * root + 1;
}
}
}
堆的插入:
堆的插入总共需要两个步骤:
1.
先将元素放入到底层空间中
(
注意:空间不够时需要扩容
)
2.
将最后新插入的节点向上调整,直到满足堆的性质
public void push(int val) {
//检查满
if (isFull()){
elem = Arrays.copyOf(elem,2*elem.length);
}
//存数据
elem[usedSize] = val;
usedSize++;
shiftUp(usedSize-1);
}
向上调整:
private void shiftUp(int child) {
int parent = (child - 1) / 2;
while (parent > 0){
if (elem[child] > elem[parent]){
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
child = parent;
parent = (child - 1) / 2;
}else{
break;
}
}
}
判断满:
public boolean isFull() {
return usedSize == elem.length;
}
堆的删除:
注意:堆的删除一定删除的是堆顶元素。
具体如下:
1.
将堆顶元素对堆中最后一个元素交换
2.
将堆中有效数据个数减少一个
3.
对堆顶元素进行向下调整
public void pollHeap() {
if (isEmpty()){
throw new HeapEmptyException("优先级队列为空!");
}
int tmp = elem[0];
elem[0] = elem[usedSize-1];
elem[usedSize-1] = tmp;
usedSize--;
shiftDown(0,usedSize);
}
判断空:
public boolean isEmpty() {
return usedSize == 0;
}
堆顶元素:
public int peekHeap() {
if (isEmpty()){
throw new HeapEmptyException("优先级队列为空!");
}
return elem[0];
}
关于PriorityQueue的使用要注意:
1. 使用时必须导入
PriorityQueue所在的包,即:
import java.util.PriorityQueue;
2. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出
ClassCastException异常
3. 不能
插入
null
对象,否则会抛出NullPointerException
4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容
5. 插入和删除元素的时间复杂度为
6. PriorityQueue底层使用了堆数据结构
7. PriorityQueue
默认情况下是小堆
---即每次获取到的元素都是最小的元素
PriorityQueue常用接口
1.优先级队列的构造
构造器 | 功能介绍 |
PriorityQueue()
|
创建一个空的优先级队列,默认容量是
11
|
PriorityQueue(int
initialCapacity)
|
创建一个初始容量为
initialCapacity
的优先级队列,注意:
initialCapacity
不能小于
1
,否则会抛
IllegalArgumentException
异常
|
PriorityQueue(Collection<?
extends E> c)
|
用一个集合来创建优先级队列
|
2.
插入
/
删除
/
获取优先级最高的元素
函数名
|
功能介绍
|
boolean offffer(E e)
|
插入元素
e
,插入成功返回
true
,如果
e
对象为空,抛出
NullPointerException
异常,时间复杂度O(logN)(以2为底),注意:空间不够时候会进行扩容
|
E peek()
|
获取优先级最高的元素,如果优先级队列为空,返回
null
|
E poll()
|
移除优先级最高的元素并返回,如果优先级队列为空,返回
null
|
int size()
|
获取有效元素的个数
|
void clear()
|
清空
|
boolean isEmpty()
|
检测优先级队列是否为空,空返回true
|