前K个高频单词(Java详解)

一、题目描述

给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。

返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序。

示例1:

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

示例2:

输入: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4
输出: ["the", "is", "sunny", "day"]
解析: "the", "is", "sunny" 和 "day" 是出现次数最多的四个单词,出现次数依次为 4, 3, 2 和 1 次。

二、题解

题目分析:

题目要求我们找到前k个出现次数最多的单词,因此我们首先要统计每个单词出现的次数,再根据每个单词出现的次数找到前k个出现次数最多的单词

思路分析:

(1)统计每个单词出现的次数

如何统计每个单词出现的次数?

我们可以使用哈希表来统计单词出现的次数,遍历数组,若单词未出现过,则将其放入哈希表中,且次数为1;若单词已出现过,则将其次数+1

代码实现:

//统计单词出现次数
//创建哈希表
Map<String, Integer> map = new HashMap<>();
//遍历数组
for(String str: words){//若单词未出现过,则将其放入哈希表中,并将次数置为1if(map.get(str) == null){map.put(str,1);}else{//若单词已在哈希表中,则将其次数+1int val = map.get(str);map.put(str,val+1);}
}

(2)找出前k个出现次数最多的单词

如何找出前k个出现次数最多的单词?

我们可以创建一个大小为k的小根堆,来找出前k个出现次数最多的单词。遍历哈希表,若堆中的单词个数小于k,则将其放入小根堆中,但当堆中的单词数等于k时,就要判断是否需要更新小根堆中的元素。

由于我们创建的是小根堆,因此堆顶元素是最小的的,我们只需判断,当前遍历到的单词的出现次数是否比堆顶单词的出现次数大,

若当前单词的出现次数大于堆顶单词的出现次数,则将堆顶元素弹出,并将当前元素放入小根堆中;

若当前单词的出现次数小于堆顶单词的出现次数,则继续遍历;

由于题目要求:当有不同的单词有相同出现频率,按照字典顺序排序,因此在当前单词的出现次数等于堆顶单词的出现次数时,我们则需要根据单词的字母顺序来判断,若当前单词的字母顺序在堆顶单词之前,则将堆顶元素弹出,并将当前元素放入小根堆中;反之,则继续遍历。

堆中的单词个数始终为k个,在遍历完成后,堆中的元素即为前k个出现次数最多的单词

具体实现:

由于我们创建的是小根堆,从堆顶弹出的元素顺序是从小到大的,因此我们在将堆中的单词放入集合后,还需要将集合反转

然而,在上述情况中,我们只考虑了堆中元素大于k时,出现两个次数相同的单词,未考虑当堆中元素小于k时,出现两个次数相同的单词

若在堆中元素小于k时出现了两个次数相同的单词,我们则需要将单词字母顺序大的元素放在堆顶,即按照单词的字母顺序创建大根堆(字母顺序大的在上,字母顺序小的在下)

为什么要将单词字母顺序大的放在堆顶?

因为我们创建的是小根堆,在弹出堆顶元素进行比较时,应将字母顺序大的元素弹出,与当前元素进行比较,且在遍历完成后,弹出元素创建集合时,应先弹出字母顺序大的,后弹出字母顺序小的,这样,在反转集合后,才能得到正确的顺序

因此,我们在堆中元素小于k时,若两元素次数不相同,根据出现次数创建小根堆,若两元素次数相同,则根据单词的字母顺序创建大根堆

代码实现:

//找出前K个出现次数最多的单词
//创建小根堆
PriorityQueue<Map.Entry<String,Integer>> minHeap = new PriorityQueue<>(new Comparator<Map.Entry<String, Integer>>() {@Overridepublic int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {//按照出现次数创建小根堆//若次数相同,则按照字典顺序创建大根堆if(o1.getValue().compareTo(o2.getValue()) == 0) {return o2.getKey().compareTo(o1.getKey());}return o1.getValue().compareTo(o2.getValue());}
});
//遍历map,将前k个高频单词放入小根堆
for(Map.Entry<String, Integer> entry: map.entrySet()){//若堆中元素小于k,将元素放入堆中if(minHeap.size() < k){minHeap.offer(entry);}else {//若堆中元素等于k,判断是否需要更新堆中元素Map.Entry<String, Integer> top = minHeap.peek();if(top.getValue().compareTo(entry.getValue()) < 0){minHeap.poll();minHeap.offer(entry);}else if(top.getValue().compareTo(entry.getValue()) == 0){if(top.getKey().compareTo(entry.getKey()) > 0){minHeap.poll();minHeap.offer(entry);}}}
}
//创建集合
List<String> ret = new ArrayList<>();
//将堆中元素弹出,并将单词放入集合中
for (int i = 0; i < k; i++) {Map.Entry<String, Integer> top = minHeap.poll();ret.add(top.getKey());
}
//反转集合
Collections.reverse(ret);

完整代码:

class Solution {public List<String> topKFrequent(String[] words, int k) {//统计单词出现次数//创建哈希表Map<String, Integer> map = new HashMap<>();//遍历数组for(String str: words){//若单词未出现过,则将其放入哈希表中,并将次数置为1if(map.get(str) == null){map.put(str,1);}else{//若单词已在哈希表中,则将其次数+1int val = map.get(str);map.put(str,val+1);}}//找出前K个出现次数最多的单词//创建小根堆PriorityQueue<Map.Entry<String,Integer>> minHeap = new PriorityQueue<>(new Comparator<Map.Entry<String, Integer>>() {@Overridepublic int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {//按照出现次数创建小根堆//若次数相同,则按照字典顺序创建大根堆if(o1.getValue().compareTo(o2.getValue()) == 0) {return o2.getKey().compareTo(o1.getKey());}return o1.getValue().compareTo(o2.getValue());}});//遍历map,将前k个高频单词放入小根堆for(Map.Entry<String, Integer> entry: map.entrySet()){//若堆中元素小于k,将元素放入堆中if(minHeap.size() < k){minHeap.offer(entry);}else {//若堆中元素等于k,判断是否需要更新堆中元素Map.Entry<String, Integer> top = minHeap.peek();if(top.getValue().compareTo(entry.getValue()) < 0){minHeap.poll();minHeap.offer(entry);}else if(top.getValue().compareTo(entry.getValue()) == 0){if(top.getKey().compareTo(entry.getKey()) > 0){minHeap.poll();minHeap.offer(entry);}}}}//创建集合List<String> ret = new ArrayList<>();//将堆中元素弹出,并将单词放入集合中for (int i = 0; i < k; i++) {Map.Entry<String, Integer> top = minHeap.poll();ret.add(top.getKey());}//反转集合Collections.reverse(ret);return ret;}
}

题目来自:

692. 前K个高频单词 - 力扣(LeetCode)

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

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

相关文章

浅谈硬件连通性测试几大优势

硬件连通性测试是确保硬件系统正常运行、提高系统可靠性和降低生产成本的关键步骤。在现代工程和制造中&#xff0c;将连通性测试纳入生产流程是一个明智的选择&#xff0c;有助于确保硬件产品的质量和性能达到最优水平。本文将介绍硬件连通性测试的主要优势有哪些! 一、提高系…

游戏测试和软件测试有什么区别

针对手游而言&#xff0c;游戏测试的本质是APP&#xff0c;所以不少手游的测试方式与APP测试异曲同工&#xff0c;然而也有所不同。APP更多的是具有一种工具&#xff0c;一款APP好不好用不重要&#xff0c;关键点在于实用。而游戏则具有一种玩具属性&#xff0c;它并不见得实用…

基于Python+requests编写的自动化测试项目-实现流程化的接口串联

框架产生目的&#xff1a;公司走的是敏捷开发模式&#xff0c;编写这种框架是为了能够满足当前这种发展模式&#xff0c;用于前后端联调之前&#xff08;后端开发完接口&#xff0c;前端还没有将业务处理完毕的时候&#xff09;以及日后回归阶段&#xff0c;方便为自己腾出学(m…

图像异常检测研究现状综述

论文标题&#xff1a;图像异常检测研究现状综述 作者&#xff1a;吕承侃 1, 2 沈 飞 1, 2, 3 张正涛 1, 2, 3 张 峰 1, 2, 3 发表日期&#xff1a;2022年6月 阅读日期 &#xff1a;2023年11月28 研究背景&#xff1a; 图像异常检测是计算机视觉领域的一个热门研究课题, 其目…

leetCode 39.组合总和 + 回溯算法 + 剪枝 + 图解 + 笔记

39. 组合总和 - 力扣&#xff08;LeetCode&#xff09; 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可以按 任意顺序 返回这些组合 can…

2015年五一杯数学建模A题不确定性条件下的最优路径问题解题全过程文档及程序

2015年五一杯数学建模 A题 不确定性条件下的最优路径问题 原题再现 目前&#xff0c;交通拥挤和事故正越来越严重的困扰着城市交通。随着我国交通运输事业的迅速发展&#xff0c;交通“拥塞”已经成为很多城市的“痼疾”。在复杂的交通环境下&#xff0c;如何寻找一条可靠、快…

HarmonyOS 数据持久化 Preferences 如何在页面中对数据进行读写

背景介绍 最近在了解并跟着官方文档尝试做一个鸿蒙app 小demo的过程中对在app中保存数据遇到些问题 特此记录下来 这里的数据持久化以 Preferences为例子展开 废话不多说 这里直接上节目(官方提供的文档示例:) 以Stage模型为例 1.明确preferences的类型 import data_prefer…

印刷企业建设数字工厂管理系统的工作内容有哪些

随着科技的不断进步&#xff0c;数字工厂管理系统在印刷企业中的应用越来越广泛。这种系统可以有效地整合企业内外资源&#xff0c;提高生产效率&#xff0c;降低生产成本&#xff0c;并为印刷企业提供更好的业务运营与管理模式。本文将从以下几个方面探讨印刷企业建设数字工厂…

如何用postman实现接口自动化测试

postman使用 开发中经常用postman来测试接口&#xff0c;一个简单的注册接口用postman测试&#xff1a; 接口正常工作只是最基本的要求&#xff0c;经常要评估接口性能&#xff0c;进行压力测试。 postman进行简单压力测试 下面是压测数据源&#xff0c;支持json和csv两个格…

Kibana部署

服务器 安装软件主机名IP地址系统版本配置KibanaElk10.3.145.14centos7.5.18042核4G软件版本&#xff1a;nginx-1.14.2、kibana-7.13.2-linux-x86_64.tar.gz 1. 安装配置Kibana &#xff08;1&#xff09;安装 [rootelk ~]# tar zxf kibana-7.13.2-linux-x86_64.tar.gz -C…

easyExcel 注解开发 快速以及简单上手 以及包含工具类

easyExcel 简单快速使用 1. mevan 这里版本我这里选的是 poi 4.1.2和 ali的easyexcel 的 3.3.1。 因为阿里easy是根据poi的依赖开发的有关系&#xff0c;两者需要对应要不然就会有很多bug和错误在运行时发生。需要版本对应&#xff0c;然而就是easy的代码也会有bug这个版本是比…

运动鞋品牌识别

一、前期工作 1. 设置GPU from tensorflow import keras from tensorflow.keras import layers,models import os, PIL, pathlib import matplotlib.pyplot as plt import tensorflow as tfgpus tf.config.list_physical_devices("GPU")if gpus:gpu0 …

Leetcode—18.四数之和【中等】

2023每日刷题&#xff08;四十一&#xff09; Leetcode—18.四数之和 实现代码 class Solution { public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> ans;sort(nums.begin(), nums.end());int n …

chatgpt prompt提示词

ChatGPT 最近十分火爆&#xff0c;今天我也来让 ChatGPT 帮我阅读一下 Vue3 的源代码。 都知道 Vue3 组件有一个 setup函数。那么它内部做了什么呢&#xff0c;今天跟随 ChatGPT 来一探究竟。 实战 1.setup setup 函数在什么位置呢&#xff0c;我们不知道他的实现函数名称&…

12 网关实战:Spring Cloud Gateway基础理论

为什么需要网关? 传统的单体架构中只有一个服务开放给客户端调用,但是微服务架构中是将一个系统拆分成多个微服务,那么作为客户端如何去调用这些微服务呢?如果没有网关的存在,只能在本地记录每个微服务的调用地址。 无网关的微服务架构往往存在以下问题: 客户端多次请求…

人机交互3——多主题多轮对话

1.主动切换 2.被动切换 3.多轮状态记忆

3.2 Windows驱动开发:内核CR3切换读写内存

CR3是一种控制寄存器&#xff0c;它是CPU中的一个专用寄存器&#xff0c;用于存储当前进程的页目录表的物理地址。在x86体系结构中&#xff0c;虚拟地址的翻译过程需要借助页表来完成。页表是由页目录表和页表组成的&#xff0c;页目录表存储了页表的物理地址&#xff0c;而页表…

使用Sui天气预言机获取全球实时天气数据

新的Sui天气预言机为全球1000多个城市的建设者提供天气数据&#xff0c;并作为一个独特的随机数生成器&#xff0c;适用于需要可信赖的随机结果的游戏和投注应用。它由基于Sui的智能合约和一个从OpenWeather API获取天气数据的后端服务组成&#xff0c;任何人都可以将天气数据集…

SpringCloudAlibaba之Nacos——详细讲解

目录 一、SpringCloudAlibaba简介 1. spring cloud alibaba 特点 2.springcloud 组件 二、环境搭建 1.构建项目并引入依赖 三、Nacos 1.什么是Nacos 2.安装Nacos 3.启动安装服务 4.访问nacos的web服务管理界面 四、开发服务注册到nacos 1.创建项目并引入依赖 2.配置注册地…

【Linux】了解进程的基础知识

进程 1. 进程的概念1.1 进程的理解1.2 Linux下的进程1.3 查看进程属性1.4 getpid和getppid 2. 创建进程3. 进程状态4. 进程优先级5. 进程切换6. 环境变量7. 本地变量与内建命令 1. 进程的概念 一个已经加载到内存中的程序&#xff0c;叫做进程&#xff08;也叫任务&#xff09…