给定一个字符串数组,再给定整数 k ,请返回出现次数前k名的字符串和对应的次数。
返回的答案应该按字符串出现频率由高到低排序。如果不同的字符串有相同出现频率,按字典序排序。
对于两个字符串,大小关系取决于两个字符串从左到右第一个不同字符的 ASCII 值的大小关系。
比如"ah1x"小于"ahb","231"<”32“
字符仅包含数字和字母
数据范围:字符串数满足 ,每个字符串长度 ,
要求:空间复杂度 ,时间复杂度
["a","b","c","b"],2
[["b","2"],["a","1"]]
"b"出现了2次,记["b","2"],"a"与"c"各出现1次,但是a字典序在c前面,记["a","1"],最后返回[["b","2"],["a","1"]]
["123","123","231","32"],2
[["123","2"],["231","1"]]
"123"出现了2次,记["123","2"],"231"与"32"各出现1次,但是"231"字典序在"32"前面,记["231","1"],最后返回[["123","2"],["231","1"]]
["abcd","abcd","abcd","pwb2","abcd","pwb2","p12"],3
[["abcd","4"],["pwb2","2"],["p12","1"]]
import java.util.*; public class Solution { /** * 题意要求是TopK问题,时间复杂度达到O(NlogK),并且相同大小要按字典排序, * 所以排序方法应该是堆排序或者快速选择排序。 * 这里排序使用小顶堆,按值排序时是从小到大。当值相同时,比较str,这里重写堆节点的比较方法, * 值相同时,str字典序大的先入堆。 * 最后,排序好的小顶堆输出k个数到结果集,结果集数组从尾部开始填充, * 这样小顶堆的数据刚好逆序,在结果集里的表现就是 按值排序是升序,值相同是字典小的在数组前面。 * return topK string * @param strings string字符串一维数组 strings * @param k int整型 the k * @return string字符串二维数组 */ public String[][] topKstrings (String[] strings, int k) { if(strings==null || strings.length==0){ return null; } HashMap<String,Integer> map = new HashMap(); for(String s : strings){ //初始化 数组中每个字符串默认出现一次 map.put(s,map.getOrDefault(s,0)+1); } // 优先队列,实现小顶堆,输出到结果集就是堆的逆序,即倒序输出小顶堆,即大顶堆 //https://blog.csdn.net/wufaliang003/article/details/82940218参考最小堆的思想 PriorityQueue<Node> minHeap = new PriorityQueue(); for(Map.Entry<String,Integer> entrySet : map.entrySet()){ Node node = new Node(entrySet.getKey(),entrySet.getValue()); //先用前k个元素生成一个小顶堆,这个小顶堆用于存储,当前最大的k个元素。 if(minHeap.size() < k){ minHeap.add(node); }else{ // 堆中元素等于 k 个时 // 当 node的值大于栈顶元素,或者值相同时node的字典小于栈顶元素 时(最小堆构建规则,就是如此) //这相当于上一个条件:先用前k个元素生成一个小顶堆,这个小顶堆用于存储,当前最大的k个元素。 //接着,从第k+1个元素开始扫描,和堆顶(堆中最小的元素)比较,如果被扫描的元素大于堆顶,则替换堆顶的元素,并调整堆, //以保证堆内的k个元素,总是当前最大的k个元素。 if(minHeap.peek().compareTo(node) < 0){ minHeap.poll(); minHeap.add(node); } } } String[][] result = new String[k][2]; //正序弹出小顶堆上面的元素,数组逆序输出 for(int i=k-1;i>=0;i--){ Node node = minHeap.poll(); result[i][0] = node.name; result[i][1] = String.valueOf(node.count); } return result; } class Node implements Comparable<Node>{ //对应上面Map里的key String name; //对应上面Map里的value int count; public Node(String name,int count){ this.name = name; this.count = count; } @Override public int compareTo(Node node){ //正常是通过Node对象里的count来比较大小的 if(this.count > node.count){ return 1; }else if(this.count < node.count){ return -1; }else{ //比如["2","2","1","1"]的情况 数组中字符串2出现2次 字符串1出现2次 这时候要求按字典顺序输出["1","2"]、["2","2"]1出现2次 2出现2次 //此时使用原生的比较器 用2个Node对象的string进行字典顺序比较 //上面的2个条件比较对象是堆顶的元素 被比较对象是要是否加入替代堆顶最小元素的元素 用的是count大小做比较 //但此时Node值相同,应该比较的是要加入的node的name字典值,小于栈顶元素 才会重新进行构建最小堆 应该反过来比较 return node.name.compareTo(this.name); } } } }
import java.util.*; public class Solution { /** * return topK string * @param strings string字符串一维数组 strings * @param k int整型 the k * @return string字符串二维数组 */ public String[][] topKstrings (String[] strings, int k) { // write code here String[][] ans=new String[k][2]; if(k==0) return ans; Map<String,Integer> maps=new HashMap<>(); for(String str:strings){ maps.put(str,maps.getOrDefault(str,0)+1); } List<Map.Entry<String,Integer>> list=new ArrayList<Map.Entry<String,Integer>>(maps.entrySet()); Collections.sort(list,new Comparator<Map.Entry<String,Integer>>(){ @Override public int compare(Map.Entry<String,Integer> o1,Map.Entry<String,Integer> o2){ if(o1.getValue()==o2.getValue()){ return o1.getKey().compareTo(o2.getKey()); } return o2.getValue()-o1.getValue(); } }); for(int i=0;i<k;i++){ ans[i][0]=list.get(i).getKey(); ans[i][1]=String.valueOf(list.get(i).getValue()); } return ans; } }方法二:用最小堆。每次把小的元素弹出,最后剩下的k个就是最大的。
import java.util.*; public class Solution { /** * return topK string * @param strings string字符串一维数组 strings * @param k int整型 the k * @return string字符串二维数组 */ public String[][] topKstrings (String[] strings, int k) { // write code here String [][]ans=new String[k][2]; if(k==0) return ans; Map<String,Integer> maps=new HashMap<>(); for(String str:strings){ maps.put(str,maps.getOrDefault(str,0)+1); } PriorityQueue<String> heap=new PriorityQueue<String>(new Comparator<String>(){ @Override public int compare(String s1,String s2){ if(maps.get(s1).equals(maps.get(s2))) return s2.compareTo(s1); return maps.get(s1)-maps.get(s2); } }); for(String str:maps.keySet()){ heap.offer(str); if(heap.size()>k) heap.poll(); } // while(!heap.isEmpty()){ // System.out.println(heap.poll()); // } for(int i=k-1;i>=0;i--){ ans[i][0]=heap.poll(); ans[i][1]=String.valueOf(maps.get(ans[i][0])); } return ans; } }注意:比较Integer的大小用equals()!!!!
public class Solution { /** * return topK string * @param strings string字符串一维数组 strings * @param k int整型 the k * @return string字符串二维数组 */ public String[][] topKstrings (String[] strings, int k) { if(k == 0){ return new String[][]{}; } // 定义hashmap 存储出现的字符串和出现的次数 Map<String,Integer> map = new HashMap<>(); for(int i=0;i<strings.length;i++){ if(map.containsKey(strings[i])){ map.put(strings[i],map.get(strings[i])+1); }else{ map.put(strings[i],1); } } // 维护K容量的小顶堆;与正常的大顶堆反过来 Queue<Node> queue = new PriorityQueue<>(k,(Node n1,Node n2)->{ if(n1.count == n2.count){ // 如果出现次数相等 则按照字典序从大到小排序(反过来) return n2.val.compareTo(n1.val); }else{ // 否则按照出现次数从小到大排序(小顶堆) return n1.count-n2.count; } }); for(Map.Entry<String,Integer> entry:map.entrySet()){ if(queue.size()<k){ queue.add(new Node(entry.getKey(),entry.getValue())); continue; } // 判断当前值和堆顶的值进行比较,如果小则丢掉。如果大则入队。 Node node = queue.peek(); if(entry.getValue() < node.count){ continue; }else if(entry.getValue() == node.count){ if(entry.getKey().compareTo(node.val) < 0){ // 堆顶出队 queue.poll(); queue.add(new Node(entry.getKey(),entry.getValue())); }else{ continue; } }else{ // 堆顶出队 queue.poll(); queue.add(new Node(entry.getKey(),entry.getValue())); } } // 队列逐个出队,倒叙输出 List<String[]> list = new ArrayList<>(); while(queue.size()>0){ Node node = queue.poll(); String[] str = new String[]{node.val,node.count+""}; list.add(str); } Collections.reverse(list); String[][] res = new String[list.size()][2]; for(int i=0;i<list.size();i++){ res[i] = list.get(i); } return res; } class Node{ String val; int count; Node(String val,int count){ this.val = val; this.count = count; } } }
public String[][] topKstrings (String[] strings, int k) { // write code here int len = strings.length; Map<String,Integer> map = new HashMap<>(); for (int i=0;i<len;++i){ map.put(strings[i], map.getOrDefault(strings[i], 0)+1); } // s[0]:值,s[1]:值出现的次数 List<String[]> list = new ArrayList<>(map.size()); for (String key : map.keySet()){ list.add(new String[]{key, String.valueOf(map.get(key))}); } Collections.sort(list, (s1,s2)->Integer.valueOf(s1[1]) == Integer.valueOf(s2[1]) ? s1[0].compareTo(s2[0]) : Integer.valueOf(s2[1])-Integer.valueOf(s1[1]) ); String[][] res = new String[k][]; for (int i=0;i<k;++i){ res[i] = list.get(i); } return res; }
function topKstrings( strings , k ) { // write code here const map = new Map() strings.forEach((item) => { if (map.has(item)) map.set(item, map.get(item) + 1) else map.set(item, 1) }) return [...map].sort((a, b) => { if (a[1] === b[1]) { if (a[0] > b[0]) return 1 else return -1 } else return b[1] - a[1] }).slice(0, k) }
public class ArrayTest { // 这个解法应该是初学者最能够理解的吧,全是基础知识,虽然比较消耗资源 public static List test(String[] arr, Integer k) { // 排重计算 Map<String, Integer> map = new HashMap<>(8); for (String key : arr) { Integer value = map.get(key); if (value == null) { map.put(key, 1); } else { value++; map.put(key, value); } } // 将map转成java对象 List<Node> nodeList = new ArrayList<>(); Set<String> keySet = map.keySet(); for (String key : keySet) { Node node = new Node(); node.setKey(key); node.setValue(map.get(key)); nodeList.add(node); } // 进行排序 Collections.sort(nodeList); // 取前K位的值 List<Node> nodeList1 = nodeList.subList(0, k); // 打印数据 System.out.println(nodeList1); return nodeList1; } } class Node implements Comparable<Node> { private String key; private Integer value; // 按格式重写toString @Override public String toString() { return "[" + key + "," + value + "]"; } // 先以value排序,再以key做自然排序 @Override public int compareTo(Node o) { int num = o.getValue() - this.getValue(); return num == 0 ? this.getKey().compareTo(o.getKey()) : num; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public Integer getValue() { return value; } public void setValue(Integer value) { this.value = value; } }
import java.util.*; public class Solution { public String[][] topKstrings (String[] strings, int k) { //统计结果 Map<String, Integer> map = new HashMap<>(); for(int i=0; i<strings.length; i++){ map.put(strings[i], map.getOrDefault(strings[i], 0)+1); } //存入List<Node>方便排序 List<Node> list = new ArrayList<>(); Set<String> set = map.keySet(); for(String key : set){ list.add(new Node(key, map.get(key))); } //排序 Collections.sort(list, new Comparator<Node>(){ public int compare(Node a, Node b){ if(a.val!=b.val) return b.val-a.val; else return a.s.compareTo(b.s); } }); //选最大k个输出 String[][] res = new String[k][2]; for(int i=0; i<k; i++){ res[i][0] = list.get(i).s; res[i][1] = String.valueOf(list.get(i).val); } return res; } } class Node{ String s; int val; public Node(String s, int val){ this.s = s; this.val = val; } }
class Solution { public: /** * return topK string * @param strings string字符串vector strings * @param k int整型 the k * @return string字符串vector<vector<>> */ struct cmp { bool operator() (pair<int,string> a, pair<int,string> b) { return a.first==b.first?a.second<b.second:a.first>b.first; } }; vector<vector<string> > topKstrings(vector<string>& strings, int k) { unordered_map<string,int> fre; priority_queue<pair<int,string>,vector<pair<int,string>>, cmp> q; //小顶堆 unordered_set<string> st; for(string& cur:strings) { fre[cur]++; } for(auto iter=fre.begin(); iter!=fre.end(); ++iter) { if(q.size()<k) { q.push(pair<int,string>{iter->second,iter->first}); }else if(q.top().first<iter->second || q.top().first==iter->second && q.top().second>iter->first) { q.pop(); q.push(pair<int,string>{iter->second,iter->first}); } } vector<vector<string>> res; while(!q.empty()) { res.push_back(vector<string>{q.top().second,to_string(q.top().first)}); q.pop(); } reverse(res.begin(),res.end()); return res; } };
function topKstrings( strings , k ) { // write code here strings.sort(); let ans = []; for(let i = 0 ; i < strings.length;){ let j = i+1; while(j<strings.length&&strings[j]===strings[i]){j++;} ans.push([strings[i],(j-i).toString()]); i = j; } ans.sort((a,b)=>b[1]-a[1]) return ans.slice(0,k); }
export function topKstrings(strings: string[], k: number): string[][] { // write code here let obj = {},target = []; strings.forEach( item => obj[item] = Reflect.has(obj,item) ? ++obj[item] : 1 ); for(let key in obj){ target.push([...[key,obj[key]]]); } target.sort(); target.sort((a,b) => b[1] - a[1]); return target.slice(0,k); }
import java.util.*; public class Solution { /** * return topK string * @param strings string字符串一维数组 strings * @param k int整型 the k * @return string字符串二维数组 */ static class Node { String key; int cnt; Node (String key, int cnt) { this.key = key; this.cnt = cnt; } } public String[][] topKstrings (String[] strings, int k) { // write code here PriorityQueue<Node> queue = new PriorityQueue<>(new Comparator<Node>() { @Override public int compare(Node o1, Node o2) { int r1 = o1.cnt - o2.cnt; return r1 == 0 ? o2.key.compareTo(o1.key) : r1; } }); HashMap<String, Integer> map = new HashMap<>(); for (String key : strings) { Integer val = map.getOrDefault(key, 0); map.put(key, val + 1); } for (String key : map.keySet()) { Integer val = map.get(key); if (queue.size() < k) { queue.add(new Node(key, val)); } else { Node node = queue.peek(); if (node.cnt < val) { queue.poll(); queue.add(new Node(key, val)); } else if (val.equals(node.cnt) && node.key.compareTo(key) > 0) { queue.poll(); queue.add(new Node(key, val)); } } } String [][] res = new String[k][2]; int idx = k - 1; while (!queue.isEmpty()) { Node node = queue.poll(); res[idx][0] = node.key; res[idx][1] = node.cnt + ""; idx--; } return res; } }
class Solution: def topKstrings(self , strings , k): res = {} result = [] max_count = 0 for i in strings: res[i] = res.get(i, 0) + 1 max_count = max(max_count, res[i]) ans = [[] for _ in range(max_count + 1)] for key, value in res.items(): ans[value].append(key) for j in range(max_count + 1): if ans[max_count - j] != []: ans[max_count - j] = sorted(ans[max_count - j]) if len(ans[max_count - j]) < k: for ele in ans[max_count - j]: result += [[ele, str(max_count - j)]] k -= len(ans[max_count - j]) else: for ele in range(k): result += [[ans[max_count - j][ele], str(max_count - j)]] return result
function topKstrings( strings , k ) { // write code here strings.sort(); const map = strings.reduce((pre, cur) => { if (pre.has(cur)) pre.set(cur, pre.get(cur) + 1); else pre.set(cur, 1); return pre; }, new Map()); const res = []; map.forEach((val, key) => res.push([key, val])); res.sort((a, b) => b[1] - a[1]); return res.slice(0, k); }
#include <unordered_map> class Solution { public: vector<vector<string> > topKstrings(vector<string>& strings, int k) { vector<vector<string>> ans(); unordered_map<string, int> m; for(string& str:strings) { ++m[str]; } if(k==0 || k > m.size()) return ans; auto cmp = [] (auto& lh, auto& rh){ return lh.second > rh.second || (lh.second == rh.second && lh.first < rh.first); }; priority_queue<pair<string, int>, vector<pair<string, int>>, decltype(cmp)> q(cmp); for(auto t:m) { pair<string, int> temp{t.first, t.second}; if(static_cast<int>(q.size() )< k) q.push(temp); else if(!cmp(q.top(), temp)) { q.pop(); q.push(temp); } } while(!q.empty()) { ans.emplace_back(vector<string>{q.top().first,to_string(q.top().second)}); q.pop(); } reverse(ans.begin(), ans.end()); return ans; } };
#include<unordered_map> #include<queue> struct Cmp{ //注意堆顶在后面,less是大顶堆,greater是小顶堆 bool operator() (pair<string,int> &a,pair<string,int> &b){ //维护一个大小为K的小顶堆才对,小的出去,让大的排在堆顶,true排在后面 if(a.second != b.second)return a.second > b.second; //相同则按照字典序排列 //字典大的排序 return a.first < b.first;//字典序小的排在后面 //return stringCmp(a.first,b.first); } }; class Solution { public: // /** * return topK string * @param strings string字符串vector strings * @param k int整型 the k * @return string字符串vector<vector<>> */ vector<vector<string>> topKstrings(vector<string>& strings, int k) { // write code here //思路没问题,但是要注意排序处理,字符串比较可以直接使用小于号 //先统计出频率 unordered_map<string,int> m; for(auto&c : strings){ m[c]++; } //优先队列:按照次数的小顶堆,存放最大的n个数 priority_queue<pair<string,int>,vector<pair<string,int>>,Cmp> q; for(auto& c : m){ if(q.size() < k){ q.push(c); } else{ //判断和堆顶的大小关系 q.push(c); q.pop(); } } k = min(k,(int)m.size()); vector<vector<string>> ans(k,vector<string>()); //可以优化一下,直接生成对应大小的数组然后逆序加入就不用reverse了 while(!q.empty()){ --k; ans[k].push_back(q.top().first); ans[k].push_back(to_string(q.top().second)); q.pop(); } return ans; } };
class Solution { public: struct cmp1 { bool operator()(pair<string, int> &a, pair<string, int> &b) { if (a.second != b.second) return a.second > b.second; else return a.first < b.first; } }; vector<vector<string>> ans; vector<vector<string>> topKstrings(vector<string> &strings, int k) { map<string, int> m; for (auto &a:strings) m[a]++; priority_queue<pair<string, int>, vector<pair<string, int>>, cmp1> p; auto ite = m.begin(); for (int i = 0; i < k; i++,ite++) p.push({ite->first, ite->second}); for (; ite != m.end(); ite++) { pair<string, int> tmp = p.top(); if ((ite->second == tmp.second && ite->first < tmp.first) || ite->second > tmp.second) { p.pop(); p.push({ite->first, ite->second}); } } ans.resize(k); for (int i = k - 1; i >= 0; i--) { auto node = p.top(); p.pop(); ans[i]={node.first,to_string(node.second)}; } return ans; } };
import java.util.*; public class Solution { /** * return topK string * @param strings string字符串一维数组 strings * @param k int整型 the k * @return string字符串二维数组 */ public String[][] topKstrings (String[] strings, int k) { Map<String, Integer> map = new HashMap<>(); for (String string : strings) { map.merge(string, 1, Integer::sum); } Set<String> set = map.keySet(); List<String> str = new ArrayList<>(set); str.sort((s1, s2) -> map.get(s1).equals(map.get(s2)) ? s1.compareTo(s2) : map.get(s2) - map.get(s1)); String[][] result = new String[k][2]; for (int i = 0; i < k; i++) { result[i] = new String[]{str.get(i), String.valueOf(map.get(str.get(i)))}; } return result; } }
vector<vector<string> > topKstrings(vector<string>& strings, int k) { vector<vector<string> > res; map<string, int> m; for (auto s : strings) ++m[s]; for (auto it = m.begin(); it != m.end(); ++it) res.push_back({it->first, to_string(it->second)}); sort(res.begin(), res.end(), [](vector<string>& a, vector<string>& b){ if (a[1] == b[1]) return a[0] < b[0]; return stoi(a[1]) > stoi(b[1]); }); res.resize(k); return res; }