【优先级队列(大顶堆 小顶堆)】【遍历哈希表键值对】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.rhkb.cn/news/253196.html

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

相关文章

Java多态原理

参考 虚方法 JVM杂记&#xff1a;对多态实现原理、虚方法表、虚方法、静态解析、动态链接的一些思考_多态和方法表的关系-CSDN博客 静态分派与动态分派 &#xff08;JVM&#xff09;Java虚拟机&#xff1a;静态分派 & 动态分派 原理解析 - 掘金 虚方法表 JVM 栈帧&am…

【C++】类和对象(3)

继续学习类和对象的最后一部分知识&#xff0c;主要有初始化列表、static成员、友元、内部类、匿名对象等。 目录 再谈构造函数 构造函数体赋值 初始化列表 explicit关键字 static成员 概念 特性 友元 友元函数 友元类 内部类 匿名对象 拷贝对象时的一些编译器优化…

跨境电商新风潮:充分发挥海外云手机的威力

在互联网行业迅速发展的大环境下&#xff0c;跨境电商、海外社交媒体营销以及游戏产业等重要领域都越来越需要借助海外云手机的协助。 特别是在蓬勃发展的跨境电商领域&#xff0c;像亚马逊、速卖通、eBay等平台&#xff0c;结合社交电商营销和短视频内容成为最有效的流量来源。…

2024-2-6-复习作业

1> 要求&#xff1a; 源代码&#xff1a; #include <stdio.h> #include <stdlib.h> void output(int arr[],int len) {for(int i0;i<len;i){printf("%d ",arr[i]);}puts(""); } void bubble_sort(int arr[],int len) {for(int i1;i<…

【Linux Day15 TCP网络通讯】

TCP网络通讯 TCP编程流程 接口介绍 socket()方法是用来创建一个套接字&#xff0c;有了套接字就可以通过网络进行数据的收发。创建套接字时要指定使用的服务类型&#xff0c;使用 TCP 协议选择流式服务&#xff08;SOCK_STREAM&#xff09;。 **bind()方法是用来指定套接字使…

JavaScript基础第二天

JavaScript基础第二天 今天我们学习if分支语句、三元表达式和switch-case语句。 1. if分支语句 1.1 语法 if (条件表达式){// 满足条件要执行的语句 } else {// 不满足条件要执行的语句 }if中的内容如果为true&#xff0c;就执行大括号的代码块&#xff0c;如果为false执行…

node.js后端+小程序前端+mongoDB(增删改查)

前言 今天我对比了以下node.js的express与python的fastAPI&#xff0c;我决定我还是出一期关于node.jsmangoDB小程序的小案例吧。 不是python的fastAPI不好用&#xff0c;因为fastAPI是python较新的技术&#xff0c;我不敢果断发出教学文章&#xff08;这件事情还是留着给pyt…

模拟请求ElasticSearch

Step1 安装chrome的这个插件 Step2 打开插件&#xff0c;GET的json填什么。 在IDEA的debug模式&#xff0c;走到Java代码的searchBuilder&#xff0c; 在这个searchBuilder变量里&#xff0c;对里面query变量点右侧 view按钮&#xff0c; IDEA里会显示出一个json&#xff…

【Qt学习笔记】(三)常用控件(持续更新)

Qt 常用控件 1 控件概述2 QWidget 控件核心属性2.1 enabled2.2 geometry2.3 window frame 的影响2.4 windowTitle2.5 window Icon2.6 windowOpacity2.7 cursor2.8 font2.9 toolTip2.10 focusPolicy2.11 stylesheet 1 控件概述 Widget是Qt中的核心概念英文原义是"小部件&q…

Nicn的刷题日常之获得月份天数

目录 1.题目描述 描述 输入描述&#xff1a; 输出描述&#xff1a; 示例1 2.解题 1.题目描述 描述 KiKi想获得某年某月有多少天&#xff0c;请帮他编程实现。输入年份和月份&#xff0c;计算这一年这个月有多少天。 输入描述&#xff1a; 多组输入&#xff0c;一行有两…

springboot Feign方式注入注解详解

一、FeignClient注解详解 FeignClient是Spring Cloud中用于声明Feign客户端的注解&#xff0c;它使得编写HTTP客户端变得更简单。通过Feign的自动化配置机制&#xff0c;可以很容易地编写HTTP API客户端。以下是FeignClient的详解&#xff1a; 作用&#xff1a;FeignClient注解…

解决SpringBoot官网创建SpringBoot项目但Java版本只能勾选17和21的情况

IDEA页面创建Spring项目&#xff0c;其实是访问spring initializr去创建项目。我们可以通过阿里云国服去间接创建Spring项目。服务器URL地址替换为 https://start.aliyun.com

BeginCTF2024 RE 部分复现

8. arc 上面一托混淆&#xff0c;左边似乎是三个东西相乘 单独取出最左边一托打印&#xff0c;可以得到大数组 接下来要解密&#xff0c;原代码非常混乱&#xff0c;我们先整理一下&#xff0c;简单去混淆 print (all([[data][a][d] e for a, b in enumerate([[int(a) for a …

【开源】SpringBoot框架开发大病保险管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统配置维护2.2 系统参保管理2.3 大病保险管理2.4 大病登记管理2.5 保险审核管理 三、系统详细设计3.1 系统整体配置功能设计3.2 大病人员模块设计3.3 大病保险模块设计3.4 大病登记模块设计3.5 保险审核模块设计 四、…

关于Django部署

首先了解一下开发环境服务器跟生产环境服务器有何不同。 一、我们通过 python manage.py runserver 启动开发环境服务器&#xff0c;这条命令背后做了哪些事情&#xff1f; 1、首先加载Django项目的设置&#xff08;settings&#xff09; 2、检查数据库迁移&#xff0c;确保数…

本地缓存Ehcache的应用实践 | 京东云技术团队

java本地缓存包含多个框架&#xff0c;其中常用的包括&#xff1a;Caffeine、Guava Cache和Ehcache&#xff0c; 其中Caffeine号称本地缓存之王&#xff0c;也是近年来被众多程序员推崇的缓存框架&#xff0c;同时也是SpringBoot内置的本地缓存实现。但是除了Caffeine之外&…

Cocos creator 3.x 刚体组件碰撞无效

Cocos creator 3.x 刚体组件碰撞无效 问题描述&#xff1a;只有一个circleCollider2D时&#xff0c;可以在碰撞时正确输出结果&#xff0c;但是当我在外围加了一个circle之后&#xff0c;期望character进入圆圈范围时就触发方法&#xff0c;此时原代码失效 import { _decorat…

Kubernetes基础(十四)-k8s网络通信

1 k8s网络类型 2 Pod网络 2.1 同一pod内不同容器通信 Pod是Kubernetes中最小的可部署单元&#xff0c;它是一个或多个紧密关联的容器的组合&#xff0c;这些容器共享同一个网络命名空间和存储卷&#xff0c;因此Pod中的所有容器都共享相同的网络命名空间和IP地址——PodIP&a…

JVM 性能调优 - 四种引用(4)

为什么会有四种引用 我们先回顾下在 Java 虚拟机内存体系(1) 中提到了的垃圾回收算法 1、引用计数法 原理:给对象添加一个引用计数器,每当有一个地方引用它,计数器的值就加一。每当有一个引用失效,计数器的值就减一。当计数器值为零时,这个对象被认为没有其他对象引用,…

【Java安全】ysoserial-URLDNS链分析

前言 Java安全中经常会提到反序列化&#xff0c;一个将Java对象转换为字节序列传输&#xff08;或保存&#xff09;并在接收字节序列后反序列化为Java对象的机制&#xff0c;在传输&#xff08;或保存&#xff09;的过程中&#xff0c;恶意攻击者能够将传输的字节序列替换为恶…