【优先级队列(大顶堆 小顶堆)】【遍历哈希表键值对】Leetcode 347 前K个高频元素

【优先级队列(大顶堆 小顶堆)】【排序】Leetcode 347 前K个高频元素

    • 1.不同排序法归纳
    • 2.大顶堆和小顶堆
    • 3.PriorityQueue操作
    • 4.PriorityQueue的升序(默认)与降序
    • 5.问题解决:找前K个最大的元素 :踢走最小的(堆顶的),加入比堆顶大的,最终就是最大的K个
    • 6.问题解决:找前K个最小的元素 :维护一个小顶堆,最后从堆顶依次弹出K个,最终就是最小的K个
  • 题目347解法

---------------🎈🎈题目链接 Leetcode 347 前K个高频元素🎈🎈-------------------



1.不同排序法归纳

在这里插入图片描述


2.大顶堆和小顶堆

是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。

大顶堆和小顶堆——非常适合在数据集中进行求前k个高频或低频结果的操作。如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。

队头种类
最大大顶堆( PriorityQueue从大到小排就是大顶堆)
最小小顶堆( PriorityQueue从小到大排就是小顶堆【默认】)

3.PriorityQueue操作

  1. 创建优先级队列【默认创建小顶堆】:
    PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
  2. 使用自定义比较器创建优先队列【创建大顶堆】:
    PriorityQueue<Integer> customPriorityQueue = new PriorityQueue<>(Collections.reverseOrder());
  3. 插入元素: 如果队列已满,则抛出一个IIIegaISlabEepeplian异常
    priorityQueue.add(5);
  4. 插入元素: 添加一个元素并返回true ,如果队列已满,则返回false
    priorityQueue.offer(5);
  5. 获取队头元素:
    Integer head = priorityQueue.peek();
  6. 弹出队头元素:
    Integer removedElement = priorityQueue.poll();
  7. 删除指定元素
    priorityQueue.remove(5);
  8. 获取队列大小:
    int size = priorityQueue.size();
  9. 遍历队列元素:
    for (Integer element : priorityQueue) { System.out.println(element); }
  10. 清空队列:
    priorityQueue.clear();

4.PriorityQueue的升序(默认)与降序

priority_queue(优先级队列),从小到大排就是小顶堆,从大到小排就是大顶堆。
默认情况下,PriorityQueue的队列是小顶堆(即从小到大【升序】),如果需要大顶堆需要用户提供比较器

// 用户自己定义的比较器:直接实现Comparator接口,然后重写该接口中的compare方法即可
class IntCmp implements Comparator<Integer>{@Overridepublic 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);System.out.println(p.peek());}
}简化写法:
PriorityQueue<int[]> my = new PriorityQueue<>((o1,o2) -> o1[1] - o2[1])
向优先级队列中传入int[]数组,排序方式根据数组的索引为[1]的元素按照升序排列

默认情况下:Java实现Comparator排序是升序,根据参数,返回值来判断是否交换
✋可参考下面的链接查看Java实现Comparator排序的方式:
🔴 Java中Comparator的升序降序使用博客
🔴 Java comparator 升序、降序、倒序从源码角度理解

在这里插入图片描述


5.问题解决:找前K个最大的元素 :踢走最小的(堆顶的),加入比堆顶大的,最终就是最大的K个

  1. 将数组中前K个元素建立一个小根堆( PriorityQueue从小到大排就是小顶堆【默认】)
  2. 然后用数组中剩下的元素和堆顶元素进行比较。

此时如果比堆顶元素大(说明当前堆中的K个元素一定不是最大的K个),就踢走堆顶的最小的,加入新元素,更新堆顶元素的值,最后比较完数组中剩下的元素,此时堆中就是前K个最大的元素。


6.问题解决:找前K个最小的元素 :维护一个小顶堆,最后从堆顶依次弹出K个,最终就是最小的K个

  1. 将数组中全部元素建立一个小根堆 (PriorityQueue从小到大排就是小顶堆【默认】)
  2. 弹出K个元素放进结果数组即可。

题目347解法

在这里插入图片描述
时间复杂度O(NlogK)
空间复杂度O(N)

1 使用hashMap存储key和value,key是元素,value是元素出现次数:
HashMap<Integer, Integer> myhashmap = new HashMap<>()
🔴for(int num:nums){myhashmap.put(num, getOrDefault(num, 0)+1);}

2 初始化优先级队列
在优先队列中存储int[ ],int[ ] 的第一个元素代表数组的值,第二个元素代表了该值出现的次数。
自定义比较器按照出现次数,从队头到队尾的顺序是从小到大排(升序),出现次数最低的在队头(相当于小顶堆)
⭐️⭐️ ⭐️可以记住(o1,o2) -> o1-o2 代表从队顶开始升序排序⭐️⭐️⭐️
⭐️⭐️ ⭐️可以记住(o1,o2) -> o1-o2 代表从队顶开始升序排序⭐️⭐️⭐️
⭐️⭐️ ⭐️可以记住(o1,o2) -> o1-o2 代表从队顶开始升序排序⭐️⭐️⭐️
在这里插入图片描述
在这里插入图片描述

🔴 PriorityQueue<int[]> pq = new PriorityQueue<>((pair1,pair2)->pair1[1]-pair2[1]);

自定义比较器(pair1, pair2) -> pair1[1] - pair2[1]
pair1和pair2都是整型数组int[ ]pair1[1] - pair2[1]表示比较的是两个整形数组中的第二个元素,且表示升序排序
*Comparator接口说明:
返回负数,形参中第一个参数排在前面(队头);返回正数,形参中第二个参数排在前面(队头)
⭐️⭐️ ⭐️可以记住(o1,o2) -> o1-o2 代表从队顶开始升序排序⭐️⭐️⭐️
在这里插入图片描述

3 使用map.entrySet() 获取key-value的set集合

for (Map.Entry<Integer, Integer> entry : map.entrySet()) {// 循环体
}

🔴map.entrySet(): 是 Map 接口中的一个方法,它返回一个键值对Set<Map.Entry<K, V>>。map 是一个实现了 Map 接口的对象,比如 HashMap 或 TreeMap。调用 entrySet() 方法会返回一个包含 Map.Entry 对象的集合。每个 Map.Entry 对象代表了 Map 中的一个键值对。

Map.Entry接口:Map.Entry 是 Java 中的一个接口,用于表示映射(Map)中的一个键值对。在 Java 中,Map 存储的是键值对的集合,而 Map.Entry 就是这个键值对的表示。Map.Entry 接口定义了一些方法,允许你访问键和值。

Map.Entry<Integer, Integer> entry: 这是增强的 for 循环的声明部分。在每次迭代中,entry 变量会被赋值为 map.entrySet() 中的一个元素,即一个键值对。

for (Map.Entry<Integer, Integer> entry : map.entrySet()) { ... }: 这是增强的 for 循环的语法,用于遍历 map.entrySet() 返回的集合中的每个元素。在循环体中,你可以使用 entry 变量来访问键和值。

举例来说,如果 map 是一个 HashMap,它包含整数键key和整数值value的映射,那么在这个循环中,**entry.getKey()**就是当前键值对的key,**entry.getValue()** 就是当前键值对的value。这种语法的好处是,它简化了遍历 Map 的过程,使得代码更加简洁,不需要显式地使用迭代器。

具体代码实现:
【程序采用判断进行,当优先队列大小小于K的时候将键值对无脑加入,大于的时候进行判断】
(判断如果当前要添加的 myentry.getValue() 大于队列顶部元素 mypriorityqueue.peek()[1] 那就弹出队列元素,添加当前的键值对入队)

class Solution {public int[] topKFrequent(int[] nums, int k) {// 初始化hashmap:用于整理统计数据和重复的元素 key:元素  value:元素出现的次数HashMap<Integer,Integer> myhashmap = new HashMap<>();// 增强for循环 将nums中数据遍历汇总到hashmap中for(int num:nums){myhashmap.put(num, myhashmap.getOrDefault(num,0)+1);}// 首先用优先级队列维护一个小顶堆 如果新元素大于堆顶元素就弹出堆顶,加入新元素PriorityQueue<int[]> mypriorityqueue = new PriorityQueue<>((o1, o2)->o1[1]-o2[1]);// 答案数组result[]int[] result = new int[k];// 遍历hashmap的键值对for(Map.Entry<Integer,Integer> myentry : myhashmap.entrySet()){// 维护一个大小为k的小顶堆,如果优先级队列中小于K个元素,那么就无脑加入就行 等于k就需要判断了if(mypriorityqueue.size() < k){mypriorityqueue.add(new int[]{myentry.getKey(),myentry.getValue()});}// 如果超过k那就要比一下是不是比堆顶元素大,如果大那么就弹出队列元素(即为目前队列中最小的)else{ if(myentry.getValue() > mypriorityqueue.peek()[1]){mypriorityqueue.poll();mypriorityqueue.add(new int[]{myentry.getKey(),myentry.getValue()});}}}for(int i = k-1; i>=0; i--){result[i] = mypriorityqueue.poll()[0];}return result;}
}

革新1:🔴for (var x : map.entrySet())
在这个上下文中,var 是 Java 10 引入的局部变量类型推断的关键字。它可以在声明变量时根据赋值语句的类型自动推断变量的类型。在这里,var 被用于迭代 map.entrySet(),其中map.entrySet()返回的是一个Set<Map.Entry<K, V>>类型。

var x 实际上是推断为 Map.Entry<Integer, Integer> 类型,这使得 x.getKey() x.getValue() 方法能够被正确调用

注意,使用 var 的情况下,编译器会根据上下文推断变量的实际类型,因此程序员无需手动指定类型。这在简化代码并提高可读性方面有一些好处,但有时也可能导致可读性下降,特别是在复杂的代码中。

革新2
Queue使用时要尽量避免Collection的add()和remove()方法,add()和remove()方法在失败的时候会抛出异常。
🔴使用offer()来加入元素,使用poll()来获取并移出元素。

【程序采用先添加进优先队列,判断队列中超过k后再弹出】

class Solution {public int[] topKFrequent(int[] nums, int k) {// 初始化hashmap:用于整理统计数据和重复的元素 key:元素  value:元素出现的次数HashMap<Integer,Integer> myhashmap = new HashMap<>();// 增强for循环 将nums中数据遍历汇总到hashmap中for(int num:nums){myhashmap.put(num, myhashmap.getOrDefault(num,0)+1);}// 首先用优先级队列维护一个小顶堆 如果新元素大于堆顶元素就弹出堆顶,加入新元素PriorityQueue<int[]> mypriorityqueue = new PriorityQueue<>((o1, o2)->o1[1]-o2[1]);// 答案数组result[]int[] result = new int[k];// 遍历hashmap的键值对for(var x : myhashmap.entrySet()){// 利用var局部变量类型推断的关键字,获取map.entrySet()返回的Map.Entry<Integer,Integer>// 可用getKey() 和 getValue()方法获取key和valueint[] temp =  new int[2];temp[0] = x.getKey();temp[1] = x.getValue();// 采用offer向队列中添加元素,如果队列满就会返回false,就不会和add()方法一样报错了mypriorityqueue.offer(temp);// 先添加后判断大小,如果超过k那就要就弹出队列中最小的元素if(mypriorityqueue.size()>k){mypriorityqueue.poll();}}for(int i = k-1; i>=0; i--){result[i] = mypriorityqueue.poll()[0];}return result;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/454819.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mixed Content: The page at ‘xxx‘ was loaded over HTTPS, but requested an insecure XMLHttpRequest end

Mixed Content: The page at xxx was loaded over HTTPS, but requested an insecure XMLHttpRequest end 报错信息报错的原因出现的问题解决办法 报错信息 Mixed Content: The page at xxx was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint xxx. Th…

挑战杯 opencv 图像识别 指纹识别 - python

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于机器视觉的指纹识别系统 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;4分 该项目较为新颖&#xff0c;适…

功能测试+自动化测试代码覆盖率统计

Jacoco 是一个开源的覆盖率工具。Jacoco 可以嵌入到 Ant 、Maven 中&#xff0c;并提供了 EclEmma Eclipse 插件,也可以使用 Java Agent 技术监控 Java 程序。很多第三方的工具提供了对 Jacoco 的集成&#xff0c;如 sonar、Jenkins、IDEA。 Jacoco 包含了多种尺度的覆盖率计数…

研究表明:论文被大V宣传后,引用次数暴涨2~3倍!

随着AI领域的迅猛发展&#xff0c;学术成果的传播方式发生了显著转变。 期刊审稿周期长&#xff0c;当你还在和审稿人battle时&#xff0c;方法先过时了。而会议虽然没有期刊长&#xff0c;但也有几个月的时间差&#xff0c;为了保护成果的创新性并扩大影响力&#xff0c;很多…

npm 上传一个自己的应用(3) 在项目中导入及使用自己上传到NPM的工具

上文 npm 上传一个自己的应用(2) 创建一个JavaScript函数 并发布到NPM 我们创建了一个函数 并发上了npm 最后 我们这里 我们可以看到它的安装指令 这里 我们可以打开一个vue项目 终端输入 我们的安装指令 npm i 自己的包 如下代码 npm i grtest我们在 node_modules目录 下…

搭建自己的私服 maven 仓库

申明&#xff1a;本文章所使用docker-compose配置文件纯属学习运用&#xff0c;非商用如有雷同请联系本人协调处理。 一、配置docker-compose.yml文件 # 指定docker-compose的版本 version: 3 services: nexus: container_name: nexus_container image: sonatype/nex…

理解进程的一些知识准备

1. 认识冯诺依曼体系结构 计算机有很多的体系结构&#xff0c;但到如今&#xff0c;冯诺依曼体系结构变成了主流。 输入设备&#xff1a;话筒、键盘、摄像头、鼠标、磁盘、网卡… 输出设备&#xff1a;声卡、显示器、打印机、显卡、网卡、磁盘… 有的设备既能作为输入设备又能…

麒麟信安服务器操作系统荣获 “2023年湖南省软件和信息技术服务业名品”

12月22日&#xff0c;由中国软件行业协会、湖南省工业和信息化厅指导&#xff0c;湖南省软件行业协会、长沙市雨花区政府主办的2023年第五届湖南省软件产业高质量发展大会暨湖南省软件行业协会年会召开。会上隆重揭晓了“2023年湖南软件行业知名软件产品和服务”奖项&#xff0…

【Linux】进程间通信 --管道通信

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法…感兴趣就关注我吧&#xff01;你定不会失望。 本篇导航 0. 进程间通信原理1. 匿名管道1.1 通信原理1.2 接口介绍 2. 命名管道2.1 接口介绍 3. 共享内存3.1 通信原理3.2 接口介绍 0. 进…

Python类与对象

目录 面向对象 定义类 创建对象 类的成员 实例变量 构造方法 实例方法 类变量 类方法 封装性 私有变量 私有方法 使用属性 继承性 Python中的继承 多继承 方法重写 多态性 继承与多态 鸭子类型测试与多态 面向对象 类和对象都是面向对象中的重要概念。面向…

嵌入式基础知识-组合逻辑与时序逻辑电路

本篇来介绍嵌入式硬件电路的相关知识&#xff1a;组合逻辑电路与时序逻辑电路 根据电路是否具有存储功能&#xff0c;将逻辑电路分为组合逻辑电路和时序逻辑电路。 1 组合逻辑电路 组合逻辑电路&#xff0c;是指在任何时刻&#xff0c;电路的输出状态只取决于同一时刻的输入…

Redis缓存过期淘汰策略详讲

前言 查看redis最大占用内存 1&#xff09;命令查看 config get memory2&#xff09;进入redis配置文件&#xff0c;查看maxmemory vim /myredis/redis.conf3&#xff09;redis默认内存多少可用 如果不设置最大内存大小或者设置最大内存大小为0&#xff0c;在64位操作系统…