前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 优先级队列

Java 优先级队列

作者头像
全栈程序员站长
发布2022-11-09 20:19:01
6600
发布2022-11-09 20:19:01
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

文章目录

Java 优先级队列

PriorityQueue简介

PriorityQueue,即优先级队列。优先级队列可以保证每次取出来的元素都是队列中的最小最大的元素(Java优先级队列默认每次取出来的为最小元素)。

大小关系: 元素的比较可以通过元素本身进行自然排序,也可以通过构造方法传入比较器进行比较。

继承关系

在这里插入图片描述
在这里插入图片描述

通过继承关系图可以知道PriorityQueueQueen接口的一个实现类,而Queen接口是Collection接口的一个实现类,因此其拥有Collection接口的基本操作,此外,队列还提供了其他的插入移除查询的操作。每个方法存在两种形式:一种是抛出异常(操作失败时),另外一种是返回一个特殊值(null或者false,取决于操作)。

在这里插入图片描述
在这里插入图片描述

PriorityQueuepeekelement操作的时间复杂度都为常数addofferremove以及poll的时间复杂度是log(n)

PriorityQueue示例

代码语言:javascript
复制
import java.util.PriorityQueue;
public class PriorityQueueTest01 { 

public static void main(String[] args) { 

PriorityQueue<Integer> queue = new PriorityQueue<>();
queue.add(11);
queue.add(33);
queue.add(22);
queue.add(55);
queue.add(44);
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
}
}

运行结果:

在这里插入图片描述
在这里插入图片描述

代码中我们依次添加1133225544五个数据,然后进行删除,通过结果我们发现,每次删除的都为队列中最小元素,即体现了优先级队列

结论: 优先级队列默认每次获取队列中最小的元素,也可以通过comparator比较器来自定义每次获取为最小还是最大。

注意: 优先级队列中不可以存储null

Comparable比较器

Comparable接口

代码语言:javascript
复制
public interface Comparable<T> { 

public int compareTo(T o);
}

该接口只存在一个public int compareTo(T o);方法,该方法表示所在的对象和o对象进行比较,返回值分三种: 1: 表示当前对象大于o对象 0: 表示当前对象等于o对象 -1: 表示当前对象小于o对象

在优先级队列中或者具有比较特征的集合中存储的对象需要实现Comparable接口

需求: 在优先级队列中存储对象学生,每个学生有idnameage三个属性,并且使优先级队列每次按照学生的id从小到大取出。

代码示例: Student类: 当前类实现了Comparable接口,即当前类提供了默认的比较方法。

代码语言:javascript
复制
public class Student implements Comparable{ 

private int id;
private String name;
private int age;
public Student(int id, String name, int age) { 

this.id = id;
this.name = name;
this.age = age;
}
public int getId() { 

return id;
}
@Override
public String toString() { 

return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Object o) { 

Student o1 = (Student)o;
return this.id - o1.id;
}
}

PriorityQueueTest类:

代码语言:javascript
复制
public class PriorityQueueTest { 

public static void main(String[] args) { 

PriorityQueue<Student> queue = new PriorityQueue<>();
queue.add(new Student(2,"王昭君",18));
queue.add(new Student(1,"吕布",19));
queue.add(new Student(5,"貂蝉",17));
queue.add(new Student(3,"赵云",20));
queue.add(new Student(4,"荆轲",18));
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
}
}

运行结果:

在这里插入图片描述
在这里插入图片描述

Comparator比较器

新需求: 如果使优先级队列按照学生id从大到小取出呢?我们很快就会想到修改Student类的compareTo方法,使return o1.id – this.id;,这样当然可以实现我们的新需求。但是有很多时候类的compareTo方法是不能修改的,比如JDK给我们提供的源代码,在不修改compareTo方法的前提下实现需求,只能用Comparator比较器了。

Comparator接口

代码语言:javascript
复制
public interface Comparator<T> { 

int compare(T o1, T o2);
}

该接口中提供了int compare(T o1, T o2)方法,该方法需要参数是两个待比较的对象 返回结果是int类型 1: 表示o1对象大于o2对象 0: 表示o1对象等于o2对象 -1: 表名o1对象小于o2对象

修改PriorityQueueTest类:

代码语言:javascript
复制
import java.util.PriorityQueue;
public class PriorityQueueTest { 

public static void main(String[] args) { 

PriorityQueue<Student> queue = new PriorityQueue<>(new Comparator<Student>() { 

@Override
public int compare(Student o1, Student o2) { 

return o2.getId() - o1.getId();
}
});
queue.add(new Student(2,"王昭君",18));
queue.add(new Student(1,"吕布",19));
queue.add(new Student(5,"貂蝉",17));
queue.add(new Student(3,"赵云",20));
queue.add(new Student(4,"荆轲",18));
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
}
}

运行结果:

在这里插入图片描述
在这里插入图片描述

底层原理

优先级队列是如何保证每次取出的是队列中最小(最大)的元素呢?查看源码,底层的存储结构为一个数组

代码语言:javascript
复制
transient Object[] queue;

表面上是一个数组结构,实际上优先级队列采用的是堆的形式来进行存储的,通过调整小根堆大根堆来保证每次取出的元素为队列中最小最大

小根堆(任意一个非叶子节点的权值,都不大于其左右子节点的权值) 大根堆(任意一个非叶子节点的权值,都大于其左右子节点的权值)

可以通过数组来实现优先级队列底层实现,图示:

在这里插入图片描述
在这里插入图片描述

对于堆的实现是基于数组来实现的,实际开辟存储空间是数组,对数据的访问按照二叉树来进行访问遍历。父节点子节点编号存在联系,父节点和子节点存在如下关系: leftNo = parentNo * 2 + 1; rightNo= parantNo * 2 + 2; parentNo = (nodeNo – 1) / 2; 通过以上的三个公式,可以轻易的计算出某个节点的父节点以及子节点的下标,这就是为什么可以使用数组来存储堆的原因。

以小根堆为例,数据如何进行调整:

插入数据 图示:

在这里插入图片描述
在这里插入图片描述

插入数据首先在有效数据的最后一个位置,即插入在某个叶子节点上,以该节点为待调整节点,和其父节点比较,如果当前节点大于父节点,符合小根堆,不用进行调整,否则需要进行调整,调整至0号根节点或者是其中某一个位置时当前节点大于父节点才终止。

源码如下:

代码语言:javascript
复制
//从下往上调整过程 k表示待插入元素位置 x表示待插入数据
private void siftUpUsingComparator(int k, E x) { 

while (k > 0) { 

//通过当前待调整节点k找到父节点
int parent = (k - 1) >>> 1;
Object e = queue[parent];
//如果此时父节点数据小于待插入节点数据,满足小根堆,break退出
if (comparator.compare(x, (E) e) >= 0)
break;
//不满足小根堆,将父节点值插入待插入节点中
queue[k] = e;
//待比较位置就指向了父节点
k = parent;
}
queue[k] = x;
}

删除数据 图示:

在这里插入图片描述
在这里插入图片描述

因为是小根堆,其堆顶元素最小,所以删除的为堆顶的元素。删除堆顶元素过程,首先记录0号下标的位置,并用最后一个元素替换0号下标的元素,当前的小根堆可能被破坏,需要对堆进行调整,从k指定的位置开始,将逐层向下与当前的左右孩子中较小的进行交换,直到x小于或者等于左右孩子中的任何一个为止。

源码如下:

代码语言:javascript
复制
//删除节点 从上往下进行调整 待比较的位置,待调整的元素x
private void siftDownUsingComparator(int k, E x) { 

//将size/2 ,从上往下调整只需要比较到size/2即可,
//因为size/2时已经到了叶子节点,无需再调整。
int half = size >>> 1;
while (k < half) { 

//找到当前节点的左孩子
int child = (k << 1) + 1;
Object c = queue[child];
int right = child + 1;//右孩子节点下标
//找到左右孩子最小的节点,将位置记录到child,值记录在c上
if (right < size && comparator.compare((E) c, (E) queue[right]) > 0)
c = queue[child = right];
//当左右孩子最小的都大于父节点x值,满足小根堆,结束调整过程
if (comparator.compare(x, (E) c) <= 0)
break;
//最小的孩子节点小于父节点,不满足小根堆,需要进行调整
queue[k] = c;
k = child;
}
queue[k] = x;
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/190151.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年9月24日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • Java 优先级队列
    • PriorityQueue简介
      • 继承关系
        • PriorityQueue示例
          • Comparable比较器
            • Comparable接口
          • Comparator比较器
            • Comparator接口
          • 底层原理
          相关产品与服务
          对象存储
          对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档