【中等】出现次数的TOPK问题-Java:原问题
分享一个大牛的人工智能教程。零基础通俗易懂风趣幽默希望你也加入到人工智能的队伍中来请轻击人工智能教程大家好欢迎来到我的网站 人工智能被认为是一种拯救世界、终结世界的技术。毋庸置疑人工智能时代就要来临了科… 继续阅读 前言https://www.captainai.net/troubleshooterpackage live.every.day.ProgrammingDesign.CodingInterviewGuide.Other; import java.util.HashMap; import java.util.Map; /** * 出现次数的TOPK问题 * * 【题目】 * 给定String类型的数组strArr再给定整数k请严格按照排名顺序打印出现次数前k名的字符串。 * * 【举例】 * strArr[1,2,3,4]k2 * No.1: 1, times: 1 * No.2: 2, times: 1 * 这种情况下所有的字符串都出现一样多随便打印任何两个字符串都可以。 * * strArr[1,1,2,3]k2 * 输出 * 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,bac]遍历后可以生成每种字符串及其相关 * 词频的哈希表如下。 * 用哈希表的每条信息可以生成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)。如果minTimestimes说明字符串str有资格进入当前k个最高 * 词频字符串的范围。而headStr应该被移出这个范围所以把当前的堆顶(headStr,minTimes)替换成(str,times)然后从堆顶 * 的位置进行堆的调整(heapify)。如果minTimestimes说明字符串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; } HashMapString, 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.EntryString, 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 */