LeetCode【347】 Top K Frequent Elements

雨点打透心脏的1/2处 2022-05-11 14:22 248阅读 0赞

Given a non-empty array of integers, return the k most frequent elements.

Example 1:

  1. Input: nums = [1,1,1,2,2,3], k = 2
  2. Output: [1,2]

Example 2:

  1. Input: nums = [1], k = 1
  2. Output: [1]

思路:

1、首先遍历数组,使用HashMap freq的存储每个数出现的频率——freq(num, 频率);

2、遍历map,对频率进行排序,这里使用优先队列priorityQueue的方式维护出现频率最多的k个数

  1. a.PriorityQueue pq的泛型为自定义的一个Pair类, pair(频率,num),因为要对频率进行比较,因此频率就作为第一个数
  2. b.优先队列用最小堆实现方式,只存储k个数,队首即为频率最小的pair
  3. c.遍历map,当pqsize小于k值时一直往里边添加pair,当=k时获得队首pair,比较pair的频率和此时entry的值,前者小则 polloffer进该entry(也就是一直维护pq为频率最多的k个数)

3、将pq中的值放入结果list中。

代码:

  1. class Solution {
  2. public List<Integer> topKFrequent(int[] nums, int k) {
  3. //1. traversing the nums to get freq of every nums
  4. Map<Integer, Integer> freq = new HashMap<>();
  5. for(int num : nums){
  6. freq.put(num, freq.getOrDefault(num, 0) + 1);
  7. }
  8. //2. sort the freq,使用最小堆来维护出现频率最多的k个数
  9. PriorityQueue<Pair> pq = new PriorityQueue<>(k, idComparator);
  10. for(Map.Entry<Integer, Integer> entry : freq.entrySet()){
  11. if(pq.size()==k){
  12. //取出pq中出现频率最少的元素,然后删除,再添加进此时的entry
  13. if(pq.peek().getFreq() < entry.getValue()){
  14. pq.poll();
  15. pq.offer(new Pair(entry.getValue(), entry.getKey()));
  16. }
  17. }else{
  18. pq.offer(new Pair(entry.getValue(), entry.getKey()));
  19. }
  20. }
  21. System.out.println(pq.size());
  22. //3. 结果
  23. List<Integer> res = new ArrayList<>();
  24. while(!pq.isEmpty()){
  25. res.add(pq.peek().getNum());
  26. pq.poll();
  27. }
  28. return res;
  29. }
  30. //自定义匿名比较器(最小堆的方式)
  31. public static Comparator<Pair> idComparator = new Comparator<Pair>(){
  32. public int compare(Pair p1, Pair p2){
  33. return p1.getFreq() - p2.getFreq();
  34. }
  35. };
  36. }
  37. class Pair{
  38. private int freq;
  39. private int num;
  40. Pair(int freq, int num){
  41. this.freq = freq;
  42. this.num = num;
  43. }
  44. public void setFreq(int a){
  45. this.freq = a;
  46. }
  47. public int getFreq(){
  48. return freq;
  49. }
  50. public void setNum(int b){
  51. this.num = b;
  52. }
  53. public int getNum(){
  54. return num;
  55. }
  56. }

时间复杂度:遍历数组O(n),优先队列O(nlogk),总:O(nlogk)

知识点:

1、使用HashMap存储某数据中元素出现的次数。

2、优先队列:维护前k大哥元素或前k小个元素。

3、遍历Map的方法:

  1. **a**.for-each循环中使用entry遍历
  2. Map<Integer, Integer> map = new HashMap<Integer, Integer>();
  3. for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
  4. entry.getKey();
  5. entry.getValue();
  6. }

b. 在for-each中循环keys或values

  1. Map<Integer, Integer> map = new HashMap<Integer, Integer>();
  2. for (int k: map.keySet()) {
  3. int value = map.get(k);
  4. }
  5. for (int v: map.values()) {
  6. ...
  7. }

c.通过iterator

  1. HashMap<Integer, Integer> map = new HashMap<>();
  2. Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
  3. while(it.hasNext()){
  4. Entry<Integer, Integer> entry = it.next();
  5. entry.getKey();
  6. entry.getValue();
  7. }

其他:

看了discuss中快的部分答案,使用桶排序完成对频率的排序。

思路:

排序部分:按数据元素出现的频次进行排序,那么“桶”的数量范围是可以确定的——桶的数量小于等于给定数组元素的个数。编号为i的桶用于存放数组中出现频次为i的元素——即编号为i的桶存放map中值等于i的键。

排序完成后,编号大的桶中元素出现的频次高,因此,我们“逆序”(先取桶编号大的桶的元素)获取桶中数据,直到获取数据的个数等于k。

discuss中代码:

  1. public List<Integer> topKFrequent(int[] nums, int k) {
  2. List<Integer>[] bucket = new List[nums.length + 1];
  3. Map<Integer, Integer> frequencyMap = new HashMap<Integer, Integer>();
  4. for (int n : nums) {
  5. frequencyMap.put(n, frequencyMap.getOrDefault(n, 0) + 1);
  6. }
  7. for (int key : frequencyMap.keySet()) {
  8. int frequency = frequencyMap.get(key);
  9. if (bucket[frequency] == null) {
  10. bucket[frequency] = new ArrayList<>();
  11. }
  12. bucket[frequency].add(key);
  13. }
  14. List<Integer> res = new ArrayList<>();
  15. for (int pos = bucket.length - 1; pos >= 0 && res.size() < k; pos--) {
  16. if (bucket[pos] != null) {
  17. res.addAll(bucket[pos]);
  18. }
  19. }
  20. return res;
  21. }

发表评论

表情:
评论列表 (有 0 条评论,248人围观)

还没有评论,来说两句吧...

相关阅读