当前位置: 首页 > news >正文

【中等】出现次数的TOPK问题-Java:原问题

分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请轻击人工智能教程大家好!欢迎来到我的网站! 人工智能被认为是一种拯救世界、终结世界的技术。毋庸置疑,人工智能时代就要来临了,科… 继续阅读 前言https://www.captainai.net/troubleshooter

package live.every.day.ProgrammingDesign.CodingInterviewGuide.Other; import java.util.HashMap; import java.util.Map; /** * 出现次数的TOPK问题 * * 【题目】 * 给定String类型的数组strArr,再给定整数k,请严格按照排名顺序打印出现次数前k名的字符串。 * * 【举例】 * strArr=["1","2","3","4"],k=2 * No.1: 1, times: 1 * No.2: 2, times: 1 * 这种情况下,所有的字符串都出现一样多,随便打印任何两个字符串都可以。 * * strArr=["1","1","2","3"],k=2 * 输出: * No.1: 1, times: 2 * No.2: 2, times: 1 * 或者输出: * No.1: 1, times: 2 * No.2: 3, times: 1 * * 【要求】 * 如果strArr长度为N,时间复杂度请达到O(Nlogk)。 * * 【进阶题目】 * 设计并实现TopKRecord结构,可以不断地向其中加入字符串,并且可以根据字符串出现的情况随时打印加入次数最多前k个字符串, * 具体为: * 1.k在TopKRecord实例生成时指定,并且不再变化(k是构造函数的参数)。 * 2.含有add(String str)方法,即向TopKRecord中加入字符串。 * 3.含有printTopK()方法,即打印加入次数最多的前k个字符串,打印有哪些字符串和对应的次数即可,不要求严格按排名顺序打印。 * * 【举例】 * TopKRecord record = new TopKRecord(2); // 打印Top2的结构 * record.add("A"); * record.printTopK(); * 此时打印: * TOP: * Str: A Times: 1 * * record.add("B"): * record.add("B"); * record.printTopK(); * 此时打印: * TOP: * Str: A Times: 1 * Str: B Times: 2 * 或者打印 * TOP: * Str: B Times: 2 * Str: A Times: 1 * * record.add("C"); * record.add("C"); * record.printTopK(); * 此时打印: * TOP: * Str: B Times: 2 * Str: C Times: 2 * 或者打印 * TOP: * Str: C Times: 2 * Str: B Times: 2 * * 【要求】 * 1.在任何时刻,add方法的时问复杂度不超过O(logk)。 * 2.在任何时刻,printTopK方法的时间复杂度不超过O(k)。 * * 【难度】 * 原问题:中等 * 进阶问题:困难 * * 【解答】 * 原问题。首先遍历strArr并统计字符串的词频,例如,strArr=["a","b","b","a","c"],遍历后可以生成每种字符串及其相关 * 词频的哈希表如下。 * 用哈希表的每条信息可以生成Node类的实例,Node类如下。 * 哈希表中有多少信息,就建立多少Node类的实例,并且依次放入堆中,具体过程为: * 1.建立一个大小为k的小根堆,这个堆放入的是Node类的实例。 * 2.遍历哈希表的每条记录,假设一条记录为(s,t),s表示一种字符串,s的词频为t,则生成Node类的实例,记为(str,times)。 * 1)如果小根堆没有满,就直按将(str,times)加入堆,然后进行建堆调整(heapInsert调整),堆中Node类实例之间都以词频 * (times)来进行比较,词频越小,位置越往上。 * 2)如果小根堆已满,说明此时小根堆已经选出k个最高词频的字符串,那么整个小根堆的堆顶自然代表已经选出的k个最高词频的字符 * 串中,词频最低的那个。堆顶的元素记为(headStr,minTimes)。如果minTimes<times,说明字符串str有资格进入当前k个最高 * 词频字符串的范围。而headStr应该被移出这个范围,所以把当前的堆顶(headStr,minTimes)替换成(str,times),然后从堆顶 * 的位置进行堆的调整(heapify)。如果minTimes>=times,说明字符串str没有资格进入当前k个最高词频字符串的范围,因为str * 的词频还不如目前选出的k个最高词频字符串中词频最少的那个,所以什么也不做。 * 3.遍历完strArr之后,小根堆里就是所有字符串中k个最高词频的字符串,但要求严格按排名打印,所以还需要根据词频从大到小完成 * k个元素间的排序。 * 遍历strArr建立哈希表的过程为O(N)。哈希表中记录的条数最多为N条,每一条记录进堆时,堆的调整时间复杂度为O(logk),所以 * 根据记录更新小根堆的过程为O(Nlogk)。k条记录排序的时间复杂度为O(klogk)。所以总的时间复杂度为 * O(N)+O(Nlogk)+O(klogk),即O(Nlogk)。 * 具体过程请参看如下代码中的printTopKAndRank方法。 * * @author Created by LiveEveryDay */ public class AppearTimesTopKProblem1 { public static class Node { public String str; public int times; public Node(String s, int t) { str = s; times = t; } } public static void printTopKAndRank(String[] arr, int topK) { if (arr == null || topK < 1) { return; } HashMap<String, Integer> map = new HashMap<>(); // 生成哈希表(字符串词频) for (int i = 0; i != arr.length; i++) { String cur = arr[i]; if (!map.containsKey(cur)) { map.put(cur, 1); } else { map.put(cur, map.get(cur) + 1); } } Node[] heap = new Node[topK]; int index = 0; // 遍历哈希表,决定每条信息是否进堆 for (Map.Entry<String, Integer> entry : map.entrySet()) { String str = entry.getKey(); int times = entry.getValue(); Node node = new Node(str, times); if (index != topK) { heap[index] = node; heapInsert(heap, index++); } else { if (heap[0].times < node.times) { heap[0] = node; heapify(heap, 0, topK); } } } // 把小根堆的所有元素按词频从大到小排序 for (int i = index - 1; i != 0; i--) { swap(heap, 0, i); heapify(heap, 0, i); } // 严格按照排名打印k条记录 for (int i = 0; i != heap.length; i++) { if (heap[i] == null) { break; } else { System.out.print("No." + (i + 1) + ": "); System.out.print(heap[i].str + ", times: "); System.out.println(heap[i].times); } } } public static void heapInsert(Node[] heap, int index) { while (index != 0) { int parent = (index - 1) >> 1; if (heap[index].times < heap[parent].times) { swap(heap, parent, index); index = parent; } else { break; } } } public static void heapify(Node[] heap, int index, int heapSize) { int left = index * 2 + 1; int right = index * 2 + 2; int smallest = index; while (left < heapSize) { if (heap[left].times < heap[index].times) { smallest = left; } if (right < heapSize && heap[right].times < heap[smallest].times) { smallest = right; } if (smallest != index) { swap(heap, smallest, index); } else { break; } index = smallest; left = index * 2 + 1; right = index * 2 + 2; } } public static void swap(Node[] heap, int index1, int index2) { Node tmp = heap[index1]; heap[index1] = heap[index2]; heap[index2] = tmp; } public static void main(String[] args) { String[] arr = {"C", "D", "D", "E", "E"}; int topK = 2; printTopKAndRank(arr, topK); } } // ------ Output ------ /* No.1: D, times: 2 No.2: E, times: 2 */
http://www.jsqmd.com/news/690821/

相关文章:

  • BEVFusion复现实战:从环境搭建到模型训练的关键报错与解决
  • node-imap 与 OAuth 认证集成:安全连接的最佳实现方案
  • STM8S项目创建后,除了main.c你还应该关注什么?详解stm8_interrupt_vector.c
  • 从《最终幻想》到你的项目:用Unity URP+面片方案,低成本搞定游戏角色头发渲染
  • Linux运维实战:命令行高效管理OSS对象存储
  • Raspberry Pi 5与Intel N100迷你PC全面对比:2023年硬件选型指南
  • React-Bootstrap-Table远程模式详解:与后端API的完美集成
  • 别再对着手册发愁了!手把手教你用IBERT搞定A7 FPGA光口自测(附TX_disable避坑点)
  • 【C++26合约编程权威指南】:20年专家亲授插件下载、环境配置与首个可运行合约Demo(含VS2025/Clang-19双平台实测)
  • 微积分极限与连续性在工程中的实战应用
  • 差分晶振四大接口模式(LVDS/LVPECL/HCSL/CML)的实战选型与电路匹配指南
  • PPO算法深度解析:从Lunar Lander到LLM微调的完整实现
  • 10分钟上手PPTAgent:从文档到精美幻灯片的完整教程
  • PLX SDK实战:手把手教你用自动化脚本搞定驱动编译与DMA性能测试
  • 【困难】出现次数的TOPK问题-Java:进阶问题
  • 免费开源质谱数据分析工具MZmine:从零开始快速掌握代谢组学研究利器
  • 腾讯云国际站实名账号LingduCloud零度云:腾讯云国际站实名账号认证教程!!!
  • ComfyUI-Impact-Pack终极指南:三步解锁AI图像增强的完整功能
  • CentOS7服务器维护:除了reboot,这几种安全重启和关机命令你用过吗?
  • 手把手教你用MSP430G2553的TA0定时器实现PWM信号分析仪(含1Hz到50kHz实测数据对比)
  • 2026年推荐几家黑龙江胶带/哈尔滨透明胶带厂家精选合集 - 品牌宣传支持者
  • 如何快速上手radian:R语言开发者的终极控制台解决方案
  • 云原生内存管理优化:Vmem架构设计与实践
  • nli-MiniLM2-L6-H768效果展示:科研基金申请书与评审意见间的逻辑呼应分析
  • 2026专业抗震成品支架哪家好?抗震成品支架、管廊支架、管廊托臂、C 型钢厂家一站式供应厂家盘点 - 栗子测评
  • 云环境LLC缓存争用检测与优化实践
  • BRDF Explorer核心功能深度解析:从Lambert到Disney BRDF的完整探索
  • BRDF Explorer代码架构解析:从Qt界面到OpenGL渲染的完整实现
  • 2026年西安地区汽车音响改装主流梯队名录解析:碑林区汽车音响升级/莲湖区汽车音响升级/莲湖区汽车音响改装/蓝田县汽车音响改装/选择指南 - 优质品牌商家
  • 【相当困难】Manacher算法-Java:原问题