我们知道队列是先进先出的结构,而优先级队列则是指元素进出顺序是有先后次序的,在出队列时,可能需要优先级高或者优先级低的元素先出队列,这种数据结构就称为
优先级队列
;该结构是线程不安全的;
类似于医院,银行,机场或者其他地方一般设有的“军人优先”的情况;
在集合框架中所处的位置:
箭头代表继承关系
!
//创建空优先级队列---默认容量为11
PriorityQueue q=new PriorityQueue();
-创建初始容量为100的优先级队列
//设置初始容量为100的优先级队列
PriorityQueue q1=new PriorityQueue(100);
Collection
中的其他容器创建优先级队列 //借助集合框架中的其他容器构造
List<Integer> list =new ArrayList<>();
list.add(1);
list.add(2);
PriorityQueue q2=new PriorityQueue(list);
boolean offer(E e)
:插入元素 e,成功返回true,当e对象为空时,抛出NullPointerException异常;
E peek()
:获取优先级最高或最低的元素,优先级队列为空时,返回null
;
E poll()
:删除优先级最高或最低的元素,优先级队列为空时,返回null
;
int size()
:获取有效元素的个数;
void clear()
:清空;
boolean isEmpty()
:检测优先级队列是否为空;
测试代码如下:
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("优先级队列为空");
}else{
System.out.println("优先级队列不为空");
}
}
public static void main(String[] args) {
method();
}
}
PriorityQueue
所在的包; import java.util.PriorityQueue;
NullPointerException
); q.offer(null);
当容量小于64时,按照
oldCapacity
的 2 倍方式扩容;
当容量大于等于64,按照oldCapacity
的1.5倍方式扩容;
当容量超过MAX_ARRAY_SIZE
,按照MAX_ARRAY_SIZE
进行扩容;
Log2(N)
;PriorityQueue
底层使用了堆数据结构;PriorityQueue
默认情况下创建的是小堆—即每次获取到的元素都是最小的元素;注:
系统默认情况下创建的是小堆,如果想要创建大堆,则需要构造比较器--------->实质
:实现 Comparator 接口,重写该接口中的 compare 方法
;
代码如下:
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就是小堆
}
});
q.offer(1);
q.offer(2);
q.offer(5);
q.offer(3);
System.out.println(q.peek()); //5
}
优先级队列底层采用了堆的数据结构,下面做介绍~~
简单说:堆就是一棵完全二叉树,且对于任意一个结点均满足:(1)当该结点大于其孩子结点时,就称为大堆;(2)当该结点小于其孩子结点时,就称为小堆;
注:将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆
;
采用顺序存储的原因
:
对于集合{ 27,15,19,18,28,34,65,49,25,37 }中的数据,如果将其创建成堆呢?
不难发现:根结点的左右子树均已满足堆的性质(除根结点外,其余结点均是小于其孩子结点的
),如下图所示:
因此,只需要将根结点**向下调整**到合适位置即可创建一个小堆;
向下调整步骤
:
parent
标记其要调整的结点,child
标记其左孩子的结点;child)就循环进行下面两步:(1)当右孩子也存在时,将左右孩子进行比较,让child
标记出找出的较小的那个孩子;(2)将parent
与较小的那个孩子进行比较,当 parent>child
标记的孩子时,就进行交换;但交换可能就会导致下面子树不满足堆的特性,因此,需要更新变量(parent=child,child=parent*2+1
),继续 b
操作,
注意:
在将某结点往下调整时,必须保证该结点的左右子树均满足堆的特性,才可以采用
;
想不明白就看图:
具体代码如下
:
public void shiftDown(int[] array){
int parent=0;
int child=2*parent+1; //child来标记左孩子
int size=array.length;
while(child<size){
// 右孩子存在时,找左右孩子中较小的孩子,用child进行标记
if(child+1 < size && array[child+1] < array[child]){
child += 1;
}
// 如果双亲比其最小的孩子还小,说明该结构已经满足堆的特性了
if (array[parent] <= array[child]) {
break;
}else{
//将双亲与较小的孩子进行交换
int temp=array[parent];
array[parent]=array[child];
array[child]=temp;
// 当parent中大的元素往下移动时,可能会造成子树不满足堆的性质,因此需要继续向下调整
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);
}
}
时间复杂度分析:
从根比较到叶子,比较的次数为完全二叉树的高度,即 时间复杂度为 O(log2(N))
;
对于上图1的特殊情况,直接将其根结点调整好即可,但如果序列为{65,37,34,49,28,19,27,18,25,15
},又该如何创建呢? 如下图2所示:
思路(任意序列建堆):
倒数第一个非叶子结点
,该结点也是最后一个结点的双亲所在的位置,而最后一个结点的位置下标为 size-1
;(size-1)-1)/2
;代码如下:
public static void createHeap(int[] array) {
// 找倒数第一个非叶子节点,从该节点位置开始往前一直到根结点,遇到一个结点,应用向下调整
int root = ((array.length-2)/2);
for (; root >= 0; root--) {
shiftDown(array,root);
}
}
(1) 将元素放入到底层空间中;
(2)将最后新插入的结点向上调整,直到满足堆的特性;
public void offer(int e){
array[size]=e;
size++;
//将插入的新元素向上调整
shiftUp(array,size-1);
}
(1)将堆顶元素与堆中最后一个元素进行交换;
(2)将堆中有效数据的个数减少一个;
(3) 对堆顶元素进行向下调整;
public Integer poll(){
int ret=array[0];
//将堆顶元素与最后一个元素交换
array[0]=array[size-1];
//堆中有效元素个数减少一个
size--;
//将堆顶元素使用向下调整到合适位置
shiftDown(array,size,0);
return ret;
}
public int peek(){
return array[0];
}
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
// 进行参数检测
if( arr==null || k <= 0){
return new int[0];
}
PriorityQueue<Integer> q = new PriorityQueue<>();
// 将数组中的元素依次放到堆中
for(int i = 0; i < arr.length; ++i){
q.offer(arr[i]);
}
// 将优先级队列的前k个元素放到数组中
int[] ret = new int[k];
for(int i = 0; i < k; ++i){
ret[i] = q.poll();
}
return ret;
}
}