队列我们已经用的很熟了,那加上优先两个字是否还是和队列一样呢?
非也,非也
优先队列(PriorityQueue):看见这个名称是不是能猜到点什么…
PriorityQueue类在Java1.5中引入。它作为 Java Collections Framework 的一部分。PriorityQueue是基于优先堆的一个无界队列,这个优先队列中的元素可以默认自然排序或者通过提供的Comparator(比较器)在队列实例化的排序。若不指定Comparator时,默认为最小堆。
优先队列不允许空值,而且不支持不可比较的对象。优先队列要求使用Java Comparable和Comparator接口给对象排序,并且在排序时会按照优先级处理其中的元素。
优先队列的大小是不受限制的,但在创建时可以指定初始大小。当我们向优先队列增加元素的时候,队列大小会自动增加。
优先队列可以放基本数据类型的包装类(如:Integer,Long等)或自定义的类对于基本数据类型的包装类,优先队列中元素默认排列顺序是升序排列,若想降序则可自己定义比较器
static Comparator<Integer> cmp = new Comparator<Integer>() {
public int compare(Integer e1, Integer e2) {
return e2 - e1;
}
};
升序和降序:
public int compare(Object o1, Object o2) {
//升序
return o1-o2;
//降序
return o2-o1;
}
};
PriorityQueue是非线程安全的,所以Java提供PriorityBlockingQueue(实现BlockingQueue接口)用于Java多线程环境。
(1):该队列是用数组实现,但是数组大小可以动态增加,容量无限,无界
(2):队列的实现不是同步的,是线程不安全的。如果多个线程中的任意线程从结构上修改了列表, 则这些线程不应同时访问 PriorityQueue实例。
若想保证线程安全可以使用PriorityBlockingQueue 类。
(3):不允许使用 null 元素。
(4):插入方法,例如:offer()、poll()、remove() 等方法时间复杂度为O(log(n)) ;
(5)remove(Object) 和 contains(Object) 时间复杂度为O(n);
(6)检索方法(peek、element 和 size)时间复杂度为常量。
(7):方法iterator()中提供的迭代器并不保证以有序的方式遍历优先级队列中的元素。如果需要按顺序遍历,可用Arrays.sort(pq.toArray())对其进行排序。
(8):可以在构造函数中指定如何排序。
例:比较矩形,长度是升序的,宽度是降序的,若长度相等,则比较宽度
输出结果
具体代码实现:
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
class Node{
int length;
int width;
public Node(int length,int width){
this.length=length;
this.width=width;
}
}
//先比长,长是升序的,若长相等。再比宽,宽是降序的
public class TestPriority {
static Comparator<Node> cNode=new Comparator<Node>() {
@Override
public int compare(Node o1, Node o2) {
if(o1.length!=o2.length){
return o1.length-o2.length; //升序
}
return o2.width-o1.width; //降序
}
};
public static void main(String[] args) {
Queue<Node> queue=new PriorityQueue<>(cNode);
Node n1=new Node(1, 2);
Node n2=new Node(3, 5);
Node n3=new Node(2, 4);
Node n4=new Node(3,4);
queue.add(n1);
queue.add(n2);
queue.add(n3);
queue.add(n4);
Node n;
while(!queue.isEmpty()){
n=queue.poll();
System.out.println("长:"+n.length+"宽:"+n.width);
}
}
}
运行结果:
在以前我们已经探讨过堆排序的问题
那么如何利用堆结构解决TopK的问题呢?
看了一些大神写的,小女子在此处参考了后稍稍总结了一下。
TopK:是指有很大的一堆数,然后我们想要得到这些数字中最大的K个数
分析:
先将这一堆数的前K个存到一个大小为k的数组
然后把这个长度为K的数组构建成一个小根堆,数组中的第一个元素肯定是这个数组中最小的。
然后遍历剩下的数字,每次比较该数字和数组中的第一个元素,若比它大就交换,然后得到修改以后的K大小的数组,重新对其进行小根堆的调整。直到把这一堆的数字遍历完,此时K大小的数组中存放的K个数就是这一堆数中最大的K个数。
具体的代码实现:
public class TopK{
public static void main(String[] args) {
int[] array={1,9,4,7,6,2,5,3,0,8};
int[] result=getTopK(array,5);
for(int i:result){
System.out.print(i+" ");
}
}
//创建k个元素的小根堆
public static int[] create(int[] array,int k){
int[] result=new int[k];
for(int i=0;i<k;i++){
result[i]=array[i];
}
for(int i=k/2;i>=0;i--){
int temp=result[i];
int child=i*2+1;
while(child<result.length){
if(child+1<result.length&&result[child+1]<result[child]){
child++;
}
if(temp<result[child]){
break;
}
result[i]=result[child];
i=child;
child=child*2+1;
}
result[i]=temp;
}
return result;
}
private static int[] getTopK(int[] array, int k) {
int[] result=create(array,k); //k个元素的小顶堆
int min=result[0];
for(int i=k+1;i<array.length;i++){
if(array[i]>min){
insert(result,array[i]);
}
}
return result;
}
private static void insert(int[] result, int data) {
result[0]=data;
for(int i=result.length/2;i>=0;i--){
int temp=result[i];
int child=i*2+1;
while(child<result.length){
if(child+1<result.length&&result[child+1]<result[child]){
child++;
}
if(temp<result[child]){
break;
}
result[i]=result[child];
i=child;
child=child*2+1;
}
result[i]=temp;
}
}
}
运行结果
那如何解决topK小的问题呢
这就要用到我们上面说到的优先队列的问题了,利用优先队列创建大根堆。
代码实现:
import java.util.*;
//topk小的问题
// 利用优先队列构建大顶堆
public class TOPK1<E extends Comparable>{
private PriorityQueue<E> queue;
private int maxsize;
public TOPK1(){}
public TOPK1(int maxsize){
this.maxsize=maxsize;
this.queue=new PriorityQueue<E>(maxsize, new Comparator<E>() {
@Override
public int compare(E o1, E o2) {
return o2.compareTo(o1); //优先队列默认是升序,为构建大顶堆,此处让其降序
}
});
}
public void add(E e){
if(queue.size()<maxsize){
queue.add(e);
}else{
if(e.compareTo(queue.peek())<0){
queue.poll();
queue.add(e);
}
}
}
//注:优先队列本来是无序的,可用list来包装,得到数据 是有序的
public List<E> sortList(){
List<E> list=new ArrayList<>(queue); //用List来包装queue
Collections.sort(list); //排序
return list;
}
public static void main(String[] args) {
TOPK1<Integer> test=new TOPK1<>(10); //选出前10个
Random random=new Random();
for(int i=150;i>=0;i--){
test.add(i);
}
while(!test.queue.isEmpty()){
System.out.println(test.queue.poll());
}
}
}
完