❣博主主页: 33的博客❣
▶️文章专栏分类:数据结构◀️
🚚我的代码仓库: 33的代码仓库🚚
🫵🫵🫵关注我带你学更多数据结构的知识
目录
1.前言
在日常生活在在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话;初中那会班主任排座位时可能会让成绩好的同学先挑座位。在这种情况下,数据结构就应该提供两个基本操作,一个是返回最高优先级对象,一个是添加新的对象,这种就是这篇文章所讲的优先级队列。
本章重点
掌握优先级队列的概念,掌握堆的概念,堆的存储方式,堆的创建,PriorityQueuede的构造,构造大根堆,PriorityQueuede扩容top-k问题的解决。
2.优先级队列
2.1概念
在普通队列中,总是遵循先进先出的规则,第一个进入队列的就第一个出队,但是许多应用中运用的是另一种队列,每次需要从队列中取出具有最高优先权的元素,这种队列就是优先级队列。
2.2优先队列实现
JDK1.8中的PriorityQueue底层使用了堆这种数据结构,而堆实际就是在完全二叉树的基础上进行了一些调整。
3.堆的概念
如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为 小堆(或大堆),即根节点小于左子树和右子树则为小即每一个将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
1.堆中某个节点的值总是不大于或不小于其父节点的值;
2.堆总是一棵完全二叉树
3.1堆的存储方式
堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储
将元素存入数组后,有以下性质:
- 如果i=0,则i表示的节点为根节点,否则i节点的双亲结点为(i-1)/2
- 如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子。
- 如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子。
3.2堆的创建
以大根堆为例::
1.采用向下调整,从最后一个父节点:(size-1-1)/2的位置开始到0,到0位置结束。向下遍历时候如果父节点大于孩子节点就交换
2.插入元素的时候,插入到最后一个再向上调整
3.出元素的时候,出的是最大的元素,那么久直接让坐标0的元素和最后一个元素进行交换,再向下调整。
public class Myheap {
public int[] arr;
int size;
public Myheap(){
arr=new int[10];
}
public void init(int[] ret){
for (int i=0;i<ret.length;i++){
arr[i]=ret[i];
size++;
}
}
public void creatHeap(){
for (int parent=(size-1-1)/2;parent>=0;parent--){
shiftdown(parent,size);
}
}
private void shiftdown(int parent, int length) {
int child=parent*2+1;
//有左孩子
while (child<length){
if (child+1<length&&arr[child+1]>arr[child]){
//child为左右节点中较大的一个
child=child+1;
}
if(arr[child]>arr[parent]){
swap(child,parent);
parent=child;
child=2*parent+1;
}else {
break;
}
}
}
private void swap(int child, int parent) {
int tmp=arr[child];
arr[child]=arr[parent];
arr[parent]=tmp;
}
public void push(int val){
if(size==arr.length){
System.out.println("满了");
return;
}
arr[size]=val;
shiftup(size);
size++;
}
private void shiftup(int child) {
int parent=(child-1)/2;
while (child>0){
if (arr[child]>arr[parent]){
swap(child,parent);
child=parent;
parent=(child-1)/2;
}else break;
}
}
public int pop(){
if (size==0){
return -1;
}
int top=arr[0];
swap(0,size-1);
size--;
shiftdown(0,size);
return top;
}
}
4.PriorityQueue
Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的。
特性:
1. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出ClassCastException异常.
2. 不能插入null对象,否则回抛出NullPointerException
3. 插入和删除元素的时间复杂对为O(
l
o
g
2
N
log_2^N
log2N)
4. PriorityQueue底层使用了堆数据结构
5. PriorityQueue默认是小根堆,每次都是获取最小元素
4.1. 构造PriorityQueue
PriorityQueue有四种构造方式:
1.带有两个参数的构造方法:(初始容量,比较器)
2.不带参数的构造,默认容量为11
3.创建初始容量为initialCapacity的构造
4.用集合来构造
4.2用PriorityQueue构造大根堆
在默认情况下,构造的堆是小根堆,如果要构造大根堆需要用户提供比较器。
我们来看一下源码:
如果有比较器:调用siftUpUsingComparator(),没有则调siftUpComparable()。
有比较器的时候,siftUpUsingComparator():
没有比较器的时候,siftUpComparable():
通过源码,我们可以发发现,父节点如果大于子节点那么就交换。否则就break,那么这样创建的就是一个小根堆。
如果我们要创建大根堆,只需要构造一个比较器,重写Compare方法就可以了
//构造比较器:
class IntCmp implements Comparator<Integer>{
@Override
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);
4.3PriorityQueue扩容
扩容方式:
注
:
如果容量小于64时,是按照oldCapacity的2倍方式扩容的
如果容量大于等于64,是按照oldCapacity的1.5倍方式扩容的
如果容量超过MAX_ARRAY_SIZE,按照MAX_ARRAY_SIZE来进行扩容
5.top-K
设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可:OJ链接
解题思路
方法一:
建立小根堆,最顶上的元素一定是最小的,所以连续出k个元素就可以得到最小的k个元素。
方法二:
建立大小为k的大根堆,把第k个元素的大小,和大根堆的堆顶元素进行比较,如果小于堆顶元素,则堆顶元素出堆,再将此元素入堆,再k++,直到把数组遍历完。
for (int i=0;i<arr.length;i++){
priorityQueue.add(arr[i]);
}
int[] ret=new int[k];
for (int i=0;i<ret.length;i++){
ret[i]=priorityQueue.poll();
}
return ret;
int[] ret=new int[k];
if (arr==null||k<=0){
return ret;
}
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
for (int i=0;i<k;i++){
priorityQueue.add(arr[i]);
}
for (int i=k;i<arr.length;i++){
if (arr[i]<priorityQueue.peek()){
priorityQueue.poll();
priorityQueue.add(arr[i]);
}
}
for (int i=0;i<ret.length;i++){
ret[i]=priorityQueue.poll();
}
return ret;
6.总结
本篇文章主要介绍了优先级队列的概念,堆的概念,堆的存储方式,堆的创建,PriorityQueuede的构造,构造大根堆,PriorityQueuede扩容top-k问题的解决。
下期预告:排序