前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >力扣LeetCode,前 K 个高频元素

力扣LeetCode,前 K 个高频元素

作者头像
别先生
发布2020-03-20 16:02:59
6200
发布2020-03-20 16:02:59
举报
文章被收录于专栏:别先生别先生

1、优先队列的经典问题,在1000000个元素中选出前100名元素,题型模式如在N个元素中选出前M个元素。

  在这里面的关键就是M远远小于N的,如果M是1,是很简单的,只需要遍历一遍,此时时间复杂度是O(n)级别的,但是此时要选出前M个元素,如果M不等于1的话,就有点麻烦了,此时也可以将100万个元素进行一下排序,对于100万个元素,使用归并排序还是快速排序,都可以在O(NlogN)的时间复杂度内完成任务,排序之后可以直接取出前一百名元素。

  但是如果使用优先队列的话,可以在O(NlogM)的时间复杂度内解决问题。此时O(NlogM)比O(NlogN)时间复杂度更好的。

  如何使用优先队列解决这个问题呢,可以使用优先队列,维护当前看到的前M个元素。对于一百万个元素,也就是对于这N个元素,肯定是要从头到尾扫描一遍的,在扫描的过程中,我们首相将这N个元素中的前M个元素放进优先队列里面,之后每次看到一个新的元素,如果这个新的元素比当前的这个优先队列中最小的那个元素还要大的话,那么,我们就将这个优先队列中最小的那个元素扔出去,取而代之的换上我们这个新的元素,用这样的方式,相当于这个优先队列中一直维护着当前可以看到的前M个元素,直到我们将这N个元素全部扫描完,在优先队列中最终留下了这M个元素就是我们要求得这个结果。

  需要注意的是这里虽然要选出前M个元素,前M个元素默认是前M个最大的元素,但是实际上需要的是一个最小堆,我们要能够非常快速的取出当前看到的前M个元素中的最小的那个元素,我们不断的将当前可以看到的前M大的元素中那个最小的元素进行替换,此时,这里需要一个最小堆,但是呢,实际上,解决这个问题,并不需要真的使用一个最小堆的,这里依然使用最大堆。

  这里面的关键就是,什么是优先级,并不是规定越大的元素优先级越高的,事实上,在这个例子中,由于每次都要先取出优先队列中最小的那个元素,所以,实质上,这里完全可以自己去定义,元素的值越小它的优先级越高,在这样的一个定义下,依然可以使用优先队列的底层是一个最大堆,却依旧完成了这个问题。大和小其实是相对的。


2、给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

示例 1:

代码语言:javascript
复制
1 输入: nums = [1,1,1,2,2,3], k = 2
2 输出: [1,2]

示例 2:

代码语言:javascript
复制
1 输入: nums = [1], k = 1
2 输出: [1]

说明:

  • 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
  • 你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。

3、自己设计的优先队列,如下所示:

代码语言:javascript
复制
 1 package com.queue;
 2 
 3 import com.maxHeap.MaxHeap;
 4 
 5 /**
 6  * 优先队列的底层实现可以使用最大堆进行实现,
 7  * 由于优先队列本身就是一个队列,所以可以复用队列的接口。
 8  *
 9  * @param <E> 由于优先队列要具有可比较性,所以要进行继承Comparable
10  */
11 public class PriorityQueue<E extends Comparable<E>> implements Queue<E> {
12 
13     // 使用最大堆实现优先队列
14     private MaxHeap<E> maxHeap;
15 
16     /**
17      * 无参构造函数,直接创建一个MaxHeap即可
18      */
19     public PriorityQueue() {
20         maxHeap = new MaxHeap<>();
21     }
22 
23     @Override
24     public void enqueue(E e) {
25         // 入队操作,直接调用最大堆的add方法
26         maxHeap.add(e);
27     }
28 
29     @Override
30     public E dequeue() {
31         // 出队操作,将最大元素提取出来
32         return maxHeap.extractMax();
33     }
34 
35     @Override
36     public E getFront() {
37         // 查看队首的元素是谁
38         // 其实对应的是最大堆对应的堆顶的元素
39         E maxHeapMax = maxHeap.findMax();
40         // 获取到队列中队首的元素。对应栈的查看栈顶的元素。
41         return maxHeapMax;
42     }
43 
44     @Override
45     public int getSize() {
46         // 直接返回最大堆中的大小
47         return maxHeap.size();
48     }
49 
50     @Override
51     public boolean isEmpty() {
52         // 直接返回最大堆中的是否为空
53         return maxHeap.isEmpty();
54     }
55 
56 
57     public static void main(String[] args) {
58         PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
59         // 优先队列入队操作
60         for (int i = 0; i < 1000; i++) {
61             priorityQueue.enqueue(i);
62         }
63 
64 
65         // 优先队列出队操作
66         for (int i = 0; i < 1000; i++) {
67             if (i % 30 == 0) {
68                 System.out.println();
69             }
70             System.out.print(priorityQueue.dequeue() + " ");
71         }
72 
73 
74     }
75 
76 }

4、解决力扣问题的代码,如下所示:

代码语言:javascript
复制
  1 package com.leetcode;
  2 
  3 import com.queue.PriorityQueue;
  4 
  5 import java.util.*;
  6 
  7 /**
  8  * 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
  9  */
 10 public class HighFrequency {
 11 
 12     /**
 13      * 私有内部类,频次类
 14      * <p>
 15      * <p>
 16      * 优先队列的泛型元素是要具有可比较性的,所以实现可比较接口
 17      */
 18     private class Frequency implements Comparable<Frequency> {
 19         public int e;// 元素内容
 20         public int frequency;// 元素的频次
 21 
 22         /**
 23          * 含参构造函数
 24          *
 25          * @param e
 26          * @param frequency
 27          */
 28         public Frequency(int e, int frequency) {
 29             this.e = e;
 30             this.frequency = frequency;
 31         }
 32 
 33         /**
 34          * 重写比较的方法
 35          * <p>
 36          * <p>
 37          * 将当前类对象和另外一个频次的类对象进行比较
 38          *
 39          * @param another
 40          * @return
 41          */
 42         @Override
 43         public int compareTo(Frequency another) {
 44             // 对于优先队列,要非常容易的取出频次较低的那个元素
 45             // 这里可以定义优先级,可以定义什么是优先级高
 46             // 这里定义,频次越低优先级越高
 47             if (this.frequency < another.frequency) {
 48                 // Java的compareTo方法,当前元素比传进来的元素大返回1,小的话,返回-1,等于的话返回0;
 49 
 50                 // 而此处的设计是,当前元素的频次小于传进来的元素,就是当前元素频次低的话,返回1,
 51                 // 这里定义的优先级高的意思,是频次低的优先级高,
 52                 // 对于优先队列,底层虽然是最大堆,取出优先级高的那个元素,但是这个优先级最高的这个元素是频次最低的那个元素。
 53                 return 1;
 54             } else if (this.frequency > another.frequency) {
 55                 // 如果当前元素大于传进去的元素,此时是返回-1,就是优先级低的
 56                 return -1;
 57             } else {
 58                 // 如果当前元素相等传进去的元素此时是返回0
 59                 return 0;
 60             }
 61         }
 62     }
 63 
 64     /**
 65      * 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
 66      *
 67      * @param nums 非空数组
 68      * @param k    前k高的元素
 69      * @return 将前k高的集合返回
 70      */
 71     public List<Integer> topKFrequent(int[] nums, int k) {
 72         // 创建一个map对象用于统计数组元素的频数
 73         Map<Integer, Integer> map = new TreeMap<Integer, Integer>();
 74         // 循环遍历数组
 75         for (int num : nums) {
 76             // 如果数组元素已经存储在map集合中
 77             if (map.containsKey(num)) {
 78                 // 此时将num的value值加一。
 79                 map.put(num, map.get(num) + 1);
 80             } else {
 81                 map.put(num, 1);
 82             }
 83         }
 84 
 85         // 此时Map集合中保存了数组中每一个元素以及每一个元素的频数
 86         // 此时,求出,前k个频数最高的元素
 87         // 由于此时,优先队列承载的元素类型是什么类型呢,此时map是键值对的形式
 88         // 要依据元素所对应的频类来决定优先级,与此同时,也关心频率对应的元素是谁
 89         // 最后,要将这些元素返回。
 90         PriorityQueue<Frequency> priorityQueue = new PriorityQueue<Frequency>();
 91         // 遍历map集合的key
 92         for (int key : map.keySet()) {
 93             // 如果优先队列的元素数是小于k的,此时还没有存够k个元素,直接进行入队
 94             if (priorityQueue.getSize() < k) {
 95                 // 直接对于key进行入队操作
 96                 priorityQueue.enqueue(new Frequency(key, map.get(k)));
 97 
 98 
 99                 // 队首的元素就是对于优先队列来说,优先级最高的那个元素,
100                 // 在这里的优先级定义下,优先级最高的就是频次最低的那个元素,
101             } else if (map.get(key) > priorityQueue.getFront().frequency) {
102                 // 此时,优先队列里面已经有k个元素了,是我们当前看到的前k个频次最高的元素,
103                 // 此时,新遍历的一个key,此时这个key的频次可能比当前这前k个频次最高的元素中
104                 // 那个频次最小的那个元素的频次更高。这种情况下,此时应该将优先队列中频次最小的那个元素替换掉。
105 
106                 // 此时,让队首元素出队
107                 priorityQueue.dequeue();
108                 // 然后让新的元素入队,
109                 priorityQueue.enqueue(new Frequency(k, map.get(key)));
110             }
111 
112         }
113 
114         // 循环遍历结束,优先队列里面剩下的就是频次最高的前K个元素。
115         // 这样的算法,时间复杂度的O(nlogk)
116         List<Integer> list = new LinkedList<Integer>();
117 //        for (int i = 0; i < priorityQueue.getSize(); i++) {
118 //            list.add(priorityQueue.dequeue().e);
119 //        }
120 
121         while (!priorityQueue.isEmpty()) {
122             list.add(priorityQueue.dequeue().e);
123         }
124         // 将符合条件的元素返回即可。
125         return list;
126     }
127 
128     public static void main(String[] args) {
129         int[] nums = new int[]{1, 1, 1, 2, 2, 3};
130         int k = 3;
131         HighFrequency highFrequency = new HighFrequency();
132         List<Integer> list = highFrequency.topKFrequent(nums, k);
133         for (int i = 0; i < list.size(); i++) {
134             System.out.println(list.get(i));
135         }
136     }
137 
138 }

5、如果使用java自带的优先队列,实现,如下所示:

代码语言:javascript
复制
  1 package com.leetcode;
  2 
  3 import java.util.*;
  4 
  5 /**
  6  * 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
  7  */
  8 public class HighFrequency2 {
  9 
 10     /**
 11      * 私有内部类,频次类
 12      * <p>
 13      * <p>
 14      * 优先队列的泛型元素是要具有可比较性的,所以实现可比较接口
 15      */
 16     private class Frequency implements Comparable<Frequency> {
 17         public int e;// 元素内容
 18         public int frequency;// 元素的频次
 19 
 20         /**
 21          * 含参构造函数
 22          *
 23          * @param e
 24          * @param frequency
 25          */
 26         public Frequency(int e, int frequency) {
 27             this.e = e;
 28             this.frequency = frequency;
 29         }
 30 
 31         /**
 32          * 重写比较的方法
 33          * <p>
 34          * <p>
 35          * 将当前类对象和另外一个频次的类对象进行比较
 36          *
 37          * @param another
 38          * @return
 39          */
 40         @Override
 41         public int compareTo(Frequency another) {
 42             // 需要注意的是,java本身的PriorityQueue优先队列默认是最小堆。
 43             // Java的compareTo方法,当前元素比传进来的元素大返回1,小的话,返回-1,等于的话返回0;
 44             if (this.frequency < another.frequency) {
 45                 // 而此处的设计是,当前元素的频次小于传进来的元素,就是当前元素频次低的话,返回-1,
 46                 return -1;
 47             } else if (this.frequency > another.frequency) {
 48                 //
 49                 return 1;
 50             } else {
 51                 //
 52                 return 0;
 53             }
 54         }
 55     }
 56 
 57     // 如何改变Java标准库中的类相应的比较的方式
 58     private class FrequencyComparator implements Comparator<Frequency> {
 59 
 60         /**
 61          * 最小堆,是最小的元素在堆顶,最大堆,最大的元素在堆顶。
 62          *
 63          * @param o1
 64          * @param o2
 65          * @return
 66          */
 67         @Override
 68         public int compare(Frequency o1, Frequency o2) {
 69             // 谁的频率小,让谁靠前
 70             // 定义a在b的前面即a小于b返回-1,a在b的后面即a大于b返回1,a和b相等返回0。
 71 
 72             // 非常简便的实现方式,任意正数,负数都可以进行代表返回结果的
 73             return o1.frequency - o2.frequency;
 74         }
 75 
 76     }
 77 
 78 
 79     /**
 80      * 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
 81      *
 82      * @param nums 非空数组
 83      * @param k    前k高的元素
 84      * @return 将前k高的集合返回
 85      */
 86     public List<Integer> topKFrequent(int[] nums, int k) {
 87         // 创建一个map对象用于统计数组元素的频数
 88         Map<Integer, Integer> map = new TreeMap<Integer, Integer>();
 89         // 循环遍历数组
 90         for (int num : nums) {
 91             // 如果数组元素已经存储在map集合中
 92             if (map.containsKey(num)) {
 93                 // 此时将num的value值加一。
 94                 map.put(num, map.get(num) + 1);
 95             } else {
 96                 map.put(num, 1);
 97             }
 98         }
 99 
100         // 需要注意的是,java本身的PriorityQueue是最小堆。
101         PriorityQueue<Frequency> priorityQueue = new PriorityQueue<Frequency>();
102         // 遍历map集合的key
103         for (int key : map.keySet()) {
104             // 如果优先队列的元素数是小于k的,此时还没有存够k个元素,直接进行入队
105             if (priorityQueue.size() < k) {
106                 // 直接对于key进行入队操作
107                 priorityQueue.add(new Frequency(key, map.get(k)));
108 
109 
110                 // 队首的元素就是对于优先队列来说,优先级最高的那个元素,
111                 // 在这里的优先级定义下,优先级最高的就是频次最低的那个元素,
112             } else if (map.get(key) > priorityQueue.peek().frequency) {
113                 // 此时,优先队列里面已经有k个元素了,是我们当前看到的前k个频次最高的元素,
114                 // 此时,新遍历的一个key,此时这个key的频次可能比当前这前k个频次最高的元素中
115                 // 那个频次最小的那个元素的频次更高。这种情况下,此时应该将优先队列中频次最小的那个元素替换掉。
116 
117                 // 此时,让队首元素出队
118                 priorityQueue.remove();
119                 // 然后让新的元素入队,
120                 priorityQueue.add(new Frequency(k, map.get(key)));
121             }
122 
123         }
124 
125         // 循环遍历结束,优先队列里面剩下的就是频次最高的前K个元素。
126         // 这样的算法,时间复杂度的O(nlogk)
127         List<Integer> list = new LinkedList<Integer>();
128         while (!priorityQueue.isEmpty()) {
129             list.add(priorityQueue.remove().e);
130         }
131         // 将符合条件的元素返回即可。
132         return list;
133     }
134 
135     public static void main(String[] args) {
136         int[] nums = new int[]{1, 1, 1, 2, 2, 3};
137         int k = 3;
138         HighFrequency2 highFrequency = new HighFrequency2();
139         List<Integer> list = highFrequency.topKFrequent(nums, k);
140         for (int i = 0; i < list.size(); i++) {
141             System.out.println(list.get(i));
142         }
143     }
144 
145 }

6、如何改变Java标准库中的类相应的比较的方式,可以自定义比较器的方式进行实现。

代码语言:javascript
复制
  1 package com.leetcode;
  2 
  3 
  4 import java.util.*;
  5 
  6 /**
  7  * 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
  8  */
  9 public class HighFrequency2 {
 10 
 11     /**
 12      * 私有内部类,频次类
 13      * <p>
 14      * <p>
 15      * 优先队列的泛型元素是要具有可比较性的,所以实现可比较接口
 16      */
 17     private class Frequency {
 18         public int e;// 元素内容
 19         public int frequency;// 元素的频次
 20 
 21         /**
 22          * 含参构造函数
 23          *
 24          * @param e
 25          * @param frequency
 26          */
 27         public Frequency(int e, int frequency) {
 28             this.e = e;
 29             this.frequency = frequency;
 30         }
 31 
 32     }
 33 
 34     // 如何改变Java标准库中的类相应的比较的方式,比较器
 35     // 如果优先队列里面存储的就是字符串,java默认字符串大小比较是按照字典序进行比较的,
 36     // 如果有一个自定义的字符串比较的方案,由于不能修改Java中字符串比较的方法的,
 37     // 此时可以使用属于自己的比较器,定义自己的字符串比较,传给优先队列就行了.
 38     private class FrequencyComparator implements Comparator<Frequency> {
 39 
 40         /**
 41          * 最小堆,是最小的元素在堆顶,最大堆,最大的元素在堆顶。
 42          *
 43          * @param o1
 44          * @param o2
 45          * @return
 46          */
 47         @Override
 48         public int compare(Frequency o1, Frequency o2) {
 49             // 谁的频率小,让谁靠前
 50             // 定义a在b的前面即a小于b返回-1,a在b的后面即a大于b返回1,a和b相等返回0。
 51 
 52             // 非常简便的实现方式,任意正数,负数都可以进行代表返回结果的
 53             return o1.frequency - o2.frequency;
 54         }
 55 
 56     }
 57 
 58 
 59     /**
 60      * 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
 61      *
 62      * @param nums 非空数组
 63      * @param k    前k高的元素
 64      * @return 将前k高的集合返回
 65      */
 66     public List<Integer> topKFrequent(int[] nums, int k) {
 67         // 创建一个map对象用于统计数组元素的频数
 68         Map<Integer, Integer> map = new TreeMap<Integer, Integer>();
 69         // 循环遍历数组
 70         for (int num : nums) {
 71             // 如果数组元素已经存储在map集合中
 72             if (map.containsKey(num)) {
 73                 // 此时将num的value值加一。
 74                 map.put(num, map.get(num) + 1);
 75             } else {
 76                 map.put(num, 1);
 77             }
 78         }
 79 
 80         // 需要注意的是,java本身的PriorityQueue是最小堆。
 81         // 可以直接将比较器,以参数的形式传递进去。
 82         PriorityQueue<Frequency> priorityQueue = new PriorityQueue<Frequency>(new FrequencyComparator());
 83         // 遍历map集合的key
 84         for (int key : map.keySet()) {
 85             // 如果优先队列的元素数是小于k的,此时还没有存够k个元素,直接进行入队
 86             if (priorityQueue.size() < k) {
 87                 // 直接对于key进行入队操作
 88                 priorityQueue.add(new Frequency(key, map.get(k)));
 89 
 90 
 91                 // 队首的元素就是对于优先队列来说,优先级最高的那个元素,
 92                 // 在这里的优先级定义下,优先级最高的就是频次最低的那个元素,
 93             } else if (map.get(key) > priorityQueue.peek().frequency) {
 94                 // 此时,优先队列里面已经有k个元素了,是我们当前看到的前k个频次最高的元素,
 95                 // 此时,新遍历的一个key,此时这个key的频次可能比当前这前k个频次最高的元素中
 96                 // 那个频次最小的那个元素的频次更高。这种情况下,此时应该将优先队列中频次最小的那个元素替换掉。
 97 
 98                 // 此时,让队首元素出队
 99                 priorityQueue.remove();
100                 // 然后让新的元素入队,
101                 priorityQueue.add(new Frequency(k, map.get(key)));
102             }
103 
104         }
105 
106         // 循环遍历结束,优先队列里面剩下的就是频次最高的前K个元素。
107         // 这样的算法,时间复杂度的O(nlogk)
108         List<Integer> list = new LinkedList<Integer>();
109         while (!priorityQueue.isEmpty()) {
110             list.add(priorityQueue.remove().e);
111         }
112         // 将符合条件的元素返回即可。
113         return list;
114     }
115 
116     public static void main(String[] args) {
117         int[] nums = new int[]{1, 1, 1, 2, 2, 3};
118         int k = 3;
119         HighFrequency2 highFrequency = new HighFrequency2();
120         List<Integer> list = highFrequency.topKFrequent(nums, k);
121         for (int i = 0; i < list.size(); i++) {
122             System.out.println(list.get(i));
123         }
124     }
125 
126 }

7、可以将内部类,比较器,声明为匿名内部类来实现这个功能。

代码语言:javascript
复制
  1 package com.leetcode;
  2 
  3 
  4 import java.util.*;
  5 
  6 /**
  7  * 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
  8  */
  9 public class HighFrequency2 {
 10 
 11     /**
 12      * 私有内部类,频次类
 13      * <p>
 14      * <p>
 15      * 优先队列的泛型元素是要具有可比较性的,所以实现可比较接口
 16      */
 17     private class Frequency {
 18         public int e;// 元素内容
 19         public int frequency;// 元素的频次
 20 
 21         /**
 22          * 含参构造函数
 23          *
 24          * @param e
 25          * @param frequency
 26          */
 27         public Frequency(int e, int frequency) {
 28             this.e = e;
 29             this.frequency = frequency;
 30         }
 31 
 32     }
 33 
 34     // 如何改变Java标准库中的类相应的比较的方式,比较器
 35     // 如果优先队列里面存储的就是字符串,java默认字符串大小比较是按照字典序进行比较的,
 36     // 如果有一个自定义的字符串比较的方案,由于不能修改Java中字符串比较的方法的,
 37     // 此时可以使用属于自己的比较器,定义自己的字符串比较,传给优先队列就行了.
 38 //    private class FrequencyComparator implements Comparator<Frequency> {
 39 //
 40 //        /**
 41 //         * 最小堆,是最小的元素在堆顶,最大堆,最大的元素在堆顶。
 42 //         *
 43 //         * @param o1
 44 //         * @param o2
 45 //         * @return
 46 //         */
 47 //        @Override
 48 //        public int compare(Frequency o1, Frequency o2) {
 49 //            // 谁的频率小,让谁靠前
 50 //            // 定义a在b的前面即a小于b返回-1,a在b的后面即a大于b返回1,a和b相等返回0。
 51 //
 52 //            // 非常简便的实现方式,任意正数,负数都可以进行代表返回结果的
 53 //            return o1.frequency - o2.frequency;
 54 //        }
 55 //
 56 //    }
 57 
 58 
 59     /**
 60      * 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
 61      *
 62      * @param nums 非空数组
 63      * @param k    前k高的元素
 64      * @return 将前k高的集合返回
 65      */
 66     public List<Integer> topKFrequent(int[] nums, int k) {
 67         // 创建一个map对象用于统计数组元素的频数
 68         Map<Integer, Integer> map = new TreeMap<Integer, Integer>();
 69         // 循环遍历数组
 70         for (int num : nums) {
 71             // 如果数组元素已经存储在map集合中
 72             if (map.containsKey(num)) {
 73                 // 此时将num的value值加一。
 74                 map.put(num, map.get(num) + 1);
 75             } else {
 76                 map.put(num, 1);
 77             }
 78         }
 79 
 80         // 需要注意的是,java本身的PriorityQueue是最小堆。
 81         // 可以直接将比较器,以参数的形式传递进去。
 82         // 这里也可以直接使用匿名内部类,来实现比较器的方法
 83         PriorityQueue<Frequency> priorityQueue = new PriorityQueue<Frequency>(new Comparator<Frequency>() {
 84 
 85             @Override
 86             public int compare(Frequency o1, Frequency o2) {
 87                 return o1.frequency - o2.frequency;
 88             }
 89         });
 90         // 遍历map集合的key
 91         for (int key : map.keySet()) {
 92             // 如果优先队列的元素数是小于k的,此时还没有存够k个元素,直接进行入队
 93             if (priorityQueue.size() < k) {
 94                 // 直接对于key进行入队操作
 95                 priorityQueue.add(new Frequency(key, map.get(k)));
 96 
 97 
 98                 // 队首的元素就是对于优先队列来说,优先级最高的那个元素,
 99                 // 在这里的优先级定义下,优先级最高的就是频次最低的那个元素,
100             } else if (map.get(key) > priorityQueue.peek().frequency) {
101                 // 此时,优先队列里面已经有k个元素了,是我们当前看到的前k个频次最高的元素,
102                 // 此时,新遍历的一个key,此时这个key的频次可能比当前这前k个频次最高的元素中
103                 // 那个频次最小的那个元素的频次更高。这种情况下,此时应该将优先队列中频次最小的那个元素替换掉。
104 
105                 // 此时,让队首元素出队
106                 priorityQueue.remove();
107                 // 然后让新的元素入队,
108                 priorityQueue.add(new Frequency(k, map.get(key)));
109             }
110 
111         }
112 
113         // 循环遍历结束,优先队列里面剩下的就是频次最高的前K个元素。
114         // 这样的算法,时间复杂度的O(nlogk)
115         List<Integer> list = new LinkedList<Integer>();
116         while (!priorityQueue.isEmpty()) {
117             list.add(priorityQueue.remove().e);
118         }
119         // 将符合条件的元素返回即可。
120         return list;
121     }
122 
123     public static void main(String[] args) {
124         int[] nums = new int[]{1, 1, 1, 2, 2, 3};
125         int k = 3;
126         HighFrequency2 highFrequency = new HighFrequency2();
127         List<Integer> list = highFrequency.topKFrequent(nums, k);
128         for (int i = 0; i < list.size(); i++) {
129             System.out.println(list.get(i));
130         }
131     }
132 
133 }

8、可以使用匿名内部类,来改变java内置的类型,改变了两个整形之间的比较逻辑。

代码语言:javascript
复制
 1 package com.leetcode;
 2 
 3 
 4 import java.util.*;
 5 
 6 /**
 7  * 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
 8  */
 9 public class HighFrequency2 {
10 
11     /**
12      * 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
13      *
14      * @param nums 非空数组
15      * @param k    前k高的元素
16      * @return 将前k高的集合返回
17      */
18     public List<Integer> topKFrequent(int[] nums, int k) {
19         // 创建一个map对象用于统计数组元素的频数
20         Map<Integer, Integer> map = new TreeMap<Integer, Integer>();
21         // 循环遍历数组
22         for (int num : nums) {
23             // 如果数组元素已经存储在map集合中
24             if (map.containsKey(num)) {
25                 // 此时将num的value值加一。
26                 map.put(num, map.get(num) + 1);
27             } else {
28                 map.put(num, 1);
29             }
30         }
31 
32         // 需要注意的是,java本身的PriorityQueue是最小堆。
33         // 可以直接将比较器,以参数的形式传递进去。
34         // 这里也可以直接使用匿名内部类,来实现比较器的方法
35         // 此时,匿名内部类就变成了topKFrequent方法内部的局部变量,此时具有变量捕获的能力,可以捕获不可改变的变量
36         PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>(new Comparator<Integer>() {
37 
38             /**
39              * 比较的方式是按照频类进行比较的
40              *
41              * 此时比较的是o1,o2,在map中的映射值的大小,就是他们的频类大小.
42              * @param o2
43              * @return
44              */
45             @Override
46             public int compare(Integer o1, Integer o2) {
47                 return map.get(o1) - map.get(o2);
48             }
49         });
50         // 遍历map集合的key
51         for (int key : map.keySet()) {
52             // 如果优先队列的元素数是小于k的,此时还没有存够k个元素,直接进行入队
53             if (priorityQueue.size() < k) {
54                 // 直接对于key进行入队操作
55                 priorityQueue.add(key);
56 
57 
58                 // 队首的元素就是对于优先队列来说,优先级最高的那个元素,
59                 // 在这里的优先级定义下,优先级最高的就是频次最低的那个元素,
60 
61                 // 此时比较的就是key的频率
62             } else if (map.get(key) > map.get(priorityQueue.peek())) {
63                 // 此时,优先队列里面已经有k个元素了,是我们当前看到的前k个频次最高的元素,
64                 // 此时,新遍历的一个key,此时这个key的频次可能比当前这前k个频次最高的元素中
65                 // 那个频次最小的那个元素的频次更高。这种情况下,此时应该将优先队列中频次最小的那个元素替换掉。
66 
67                 // 此时,让队首元素出队
68                 priorityQueue.remove();
69                 // 然后让新的元素入队,
70                 priorityQueue.add(key);
71             }
72 
73         }
74 
75         // 循环遍历结束,优先队列里面剩下的就是频次最高的前K个元素。
76         // 这样的算法,时间复杂度的O(nlogk)
77         List<Integer> list = new LinkedList<Integer>();
78         while (!priorityQueue.isEmpty()) {
79             list.add(priorityQueue.remove());
80         }
81         // 将符合条件的元素返回即可。
82         return list;
83     }
84 
85     public static void main(String[] args) {
86         int[] nums = new int[]{1, 1, 1, 2, 2, 3};
87         int k = 3;
88         HighFrequency2 highFrequency = new HighFrequency2();
89         List<Integer> list = highFrequency.topKFrequent(nums, k);
90         for (int i = 0; i < list.size(); i++) {
91             System.out.println(list.get(i));
92         }
93     }
94 
95 }
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-03-18 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档