遨游优先队列与topK

队列我们已经用的很熟了,那加上优先两个字是否还是和队列一样呢?
非也,非也
优先队列(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());
        }

    }
}

  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值