Java中关于优先队列PriorityQueue的使用及相关方法

 更新时间:2023年08月10日 10:23:11   作者:你的代码没bug  
这篇文章主要介绍了Java中关于优先队列PriorityQueue的使用及相关方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

Java中优先队列PriorityQueue的使用

简单使用

PriorityQueue<Integer> p = new PriorityQueue<>();

优先级队列从队头取元素,从队尾添加元素。

默认情况下,队列中的数据是升序排序。

PriorityQueue<Integer> p = new PriorityQueue<>();
p.offer(5);
p.offer(1);
p.offer(3);
p.offer(6);
p.offer(8);
while(!p.isEmpty()) {
	System.out.println(p.poll());
}

运行结果:

1
3
5
6
8

方法

和队列的方法基本一样

自定义排序规则

以下代码改变排序规则,从大到小:

PriorityQueue<Integer> queue=new PriorityQueue<>((o1,o2)->o2-o1);
PriorityQueue<Integer> queue= new PriorityQueue<>(Comparator.reverseOrder());

根据HashMap的value值对HashMap的键值对排序

public static void main(String[] args) {
	HashMap<Integer, Integer> map = new HashMap<>();
	map.put(1, 2);
	map.put(3, 4);
	map.put(5, 6);
	Set<Map.Entry<Integer, Integer>> entries = map.entrySet();//获取键值对
	//自定义优先队列的排序规则,队列内存放的数据类型是键值对
	//o1,o2都是键值对这样的对象
	PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>((o1, o2) -> {
		return o1.getValue() - o2.getValue();//根据值升序排序
	});
	for (Map.Entry<Integer, Integer> entry : entries) {
		queue.offer(entry);//将键值对放到优先队列中
    }
	while(!queue.isEmpty()) {
		System.out.println(queue.poll().getValue());
	}
}

运行结果:

2
4
6

优先队列PriorityQueue (大根堆/小根堆/TopK问题)

PriorityQueue是从JDK1.5开始提供的新的数据结构接口,它是一种基于优先级堆的极大优先级队列。优先级队列是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权的元素。

默认情况下,PriorityQueue是小顶堆,如代码所示

public static void main(String[] args) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        queue.offer(6);
        while (!queue.isEmpty()){
            System.out.print(queue.poll() + " ");
        }
    }

输出结果:

我们也可以通过以下方式来构造大顶堆

public static void main(String[] args) {
  PriorityQueue<Integer> queue = new PriorityQueue<>((o1,o2) -> o2 - o1);//降序
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        queue.offer(6);
        while (!queue.isEmpty()){
            System.out.print(queue.poll() + " ");
        }
    }

输出结果:

Top K问题,力扣347和力扣692:

力扣347:给你一个整数数组  nums  和一个整数  k  ,请你返回其中出现频率前  k  高的元素。你可以按 任意顺序 返回答案。

示例:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

代码如下:注意构造小顶堆的时候(o1,o2)-> o1.getValue() - o2.getValue() 不能省略。

public int[] topKFrequent(int[] nums, int k) {
        //key为元素值,value为出现频率
        HashMap<Integer,Integer> map = new HashMap<>();
        PriorityQueue<Map.Entry<Integer, Integer>> queue 
        = new PriorityQueue<>((o1,o2)-> o1.getValue() - o2.getValue());
        int[] res = new int[k];
        for (int num : nums) {
            map.put(num,map.getOrDefault(num,0) + 1);
        }
        Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
        for (Map.Entry<Integer, Integer> entry : entries) {
            queue.offer(entry);
            if(queue.size() > k){
                queue.poll();
            }
        }
        //留下的都是出现频率最高的
        for(int i = 0; i < k; i++){
            Map.Entry<Integer, Integer> poll = queue.poll();
            res[i] = poll.getKey();
        }
        return res;

力扣692:给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序排序

示例:

输入: words = ["i", "love", "leetcode", "i", "love", "coding"], k = 2
输出: ["i", "love"]
解析: "i" 和 "love" 为出现次数最多的两个单词,均为2次。注意,按字母顺序 "i" 在 "love" 之前。

这题比上题难一点,因为当两个单词出现次数一致时还要考虑按字典序排序,其次是答案需要从高到底排序,这里主要体现在优先队列PriorityQueue的构造上。

代码如下:

public List<String> topKFrequent(String[] words, int k) {
        List<String> res = new ArrayList<>();
        //key为元素,value为出现频率
        HashMap<String,Integer> map = new HashMap<>();
        for (String word : words) {
            map.put(word,map.getOrDefault(word,0) + 1);
        }
        PriorityQueue<Map.Entry<String,Integer>> queue = new PriorityQueue<>(
                ((o1, o2) -> {
                    if(o1.getValue() == o2.getValue()){
                        return o2.getKey().compareTo(o1.getKey());
                    }
                    return o1.getValue() - o2.getValue();
                })
        );
        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        for (Map.Entry<String, Integer> entry : entries) {
            queue.offer(entry);
            if(queue.size() > k){
                queue.poll();
            }
        }
        for(int i = 0; i < k; i++){
            res.add(queue.poll().getKey());
        }
        Collections.reverse(res);
        return res;
    }

注意o2.getKey().compareTo(o1.getKey())是按字典倒序,为什么要这么做呢,是因为我们这里用到小顶堆,所以每次poll出来的都是最小频率元素,最后需要reverse一下,为了配合这个,所以我们构造优先队列的时候采用字典倒序。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java设计模式中的设计原则之合成复用原则详解

    Java设计模式中的设计原则之合成复用原则详解

    这篇文章主要介绍了Java设计模式中的设计原则之合成复用原则详解,原则是尽量使用合成/聚合的方式,而不是使用继承聚合关系表示的是整体和部分的关系,整体与部分可以分开,可以理解为成员变量和当前类的关系就是聚合关系,需要的朋友可以参考下
    2023-11-11
  • 关于Java中的CAS如何使用

    关于Java中的CAS如何使用

    这篇文章主要介绍了关于Java中的CAS如何使用,CAS是Compare And Swap(比较并交换)的缩写,是一种非阻塞式并发控制技术,用于保证多个线程在修改同一个共享资源时不会出现竞争条件,从而避免了传统锁机制的各种问题,需要的朋友可以参考下
    2023-09-09
  • 深度理解Java中volatile的内存语义

    深度理解Java中volatile的内存语义

    前面我们已经讲过了volatile的作用、底层实现与内存屏障,下面就总结一下整个流程,文中有非常详细的介绍及图文示例,需要的朋友可以参考下
    2021-06-06
  • Spring MVC--拦截器实现和用户登陆例子

    Spring MVC--拦截器实现和用户登陆例子

    本文主要介绍了Spring MVC--拦截器实现和用户登陆例子,具有很好的参考价值,下面跟着小编一起来看下吧
    2017-03-03
  • JMeter中的后端监听器的实现

    JMeter中的后端监听器的实现

    本文主要介绍了JMeter中的后端监听器的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • java后端如何调用第三方接口(往header和body中的参数传参)

    java后端如何调用第三方接口(往header和body中的参数传参)

    这篇文章主要介绍了java后端如何调用第三方接口(往header和body中的参数传参),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Java定时任务Timer、TimerTask与ScheduledThreadPoolExecutor详解

    Java定时任务Timer、TimerTask与ScheduledThreadPoolExecutor详解

    这篇文章主要介绍了Java定时任务Timer、TimerTask与ScheduledThreadPoolExecutor详解,  定时任务就是在指定时间执行程序,或周期性执行计划任务,Java中实现定时任务的方法有很多,本文从从JDK自带的一些方法来实现定时任务的需求,需要的朋友可以参考下
    2024-01-01
  • Java数据结构之图的基础概念和数据模型详解

    Java数据结构之图的基础概念和数据模型详解

    在现实生活中,有许多应用场景会包含很多点以及点点之间的连接,而这些应用场景我们都可以用即将要学习的图这种数据结构去解决。本文主要介绍了图的基础概念和数据模型,感兴趣的可以了解一下
    2022-11-11
  • SpringBoot项目启动错误:找不到或无法加载主类的三种解决方法

    SpringBoot项目启动错误:找不到或无法加载主类的三种解决方法

    在开发SpringBoot应用时,经常可能会遇到一个启动错误:“错误:找不到或无法加载主类 com.example.controller.demo.DemoApplication”,本文将介绍三种解决这一问题的方法,需要的朋友可以参考下
    2024-10-10
  • Java 8中的Collectors API介绍

    Java 8中的Collectors API介绍

    这篇文章主要介绍了Java 8中的Collectors API,Stream.collect()是Java 8的流API的终端方法之一。它允许我们对流实例中保存的数据元素执行可变折叠操作,下文相关内容需要的小伙伴可以参考一下
    2022-04-04

最新评论