• 力扣LeetCode,前 K 个高频元素


    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、给定一个非空的整数数组,返回其中出现频率前 高的元素。

    示例 1:

    1 输入: nums = [1,1,1,2,2,3], k = 2
    2 输出: [1,2]

    示例 2:

    1 输入: nums = [1], k = 1
    2 输出: [1]

    说明:

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

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

     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、解决力扣问题的代码,如下所示:

      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自带的优先队列,实现,如下所示:

      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标准库中的类相应的比较的方式,可以自定义比较器的方式进行实现。

      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、可以将内部类,比较器,声明为匿名内部类来实现这个功能。

      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内置的类型,改变了两个整形之间的比较逻辑。

     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 }
  • 相关阅读:
    提高CRM系统实施成功率
    CRM销售管理软件实施的误区
    ERP、CRM、SCM之间的区别
    选择CRM系统的四个步骤
    [导入]163相册验证码图片的识别手记之一 去除干扰
    [导入]C#中WebService里的回车符"\r"丢失问题
    [导入]文件同步精灵(初版)
    [导入]163相册验证码图片的识别手记之二 识别
    [导入]电信对我们的侵权行为如何能得到法律保护?
    [导入]认父亲的DbParameter!!
  • 原文地址:https://www.cnblogs.com/biehongli/p/12515904.html
Copyright © 2020-2023  润新知