• leetcode hot 100


    347. 前 K 个高频元素

    题目描述

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

    示例 1:

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

    示例 2:

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

    提示:

    你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
    你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
    题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
    你可以按任意顺序返回答案。

    思路一:HashMap 计数 + 排序

    使用hashmap对每个元素进行出现次数统计

    对hashmap根据value进行排序,排序前要先把hashmap中的所有entry都存入到一个list中,只有在list中才可以使用Collections.sort()进行排序

    保存前k个元素到数组

     1 class Solution {
     2     public int[] topKFrequent(int[] nums, int k) {
     3         // 使用hashmap对每个元素进行出现次数统计
     4         HashMap<Integer, Integer> map = new HashMap<>();
     5         for(int num : nums){
     6             if(map.containsKey(num)){
     7                 map.put(num, map.get(num)+1);
     8             }else{
     9                 map.put(num, 1);
    10             }
    11         }
    12 
    13         // 对hashmap根据value进行排序
    14         // 先把hashmap中的所有entry都存入到一个list中,只有在list中才可以使用Collections.sort()进行排序
    15         List<Map.Entry<Integer, Integer>> list = new ArrayList<>();
    16         list.addAll(map.entrySet());
    17 
    18         Collections.sort(list, new Comparator<Map.Entry<Integer,Integer>>(){
    19             public int compare(Map.Entry<Integer,Integer> o1, Map.Entry<Integer,Integer> o2){
    20                 return o2.getValue() - o1.getValue();
    21             }
    22         });
    23 
    24         // 保存前k个元素到数组
    25         int[] res = new int[k];
    26         int i = 0;
    27         for(Map.Entry<Integer,Integer> en : list){
    28             res[i++] = en.getKey();
    29             if(i >= k){
    30                 break;
    31             }
    32         }
    33         return res;
    34     }
    35 }

    leetcode 执行用时:18 ms > 45.17%, 内存消耗:41.1 MB > 93.84%

    复杂度分析:

    时间复杂度:O(nlogn), 使用Hashmap进行计数的复杂度为O(n), 随后对hashmap进行排序,虽然其中的键值对可能没有n那么多,但是在最坏情况下键值对的个数为n, 所以排序的时间复杂度为O(nlogn)

    空间复杂度:O(2n); 需要一个hashMap存储每个值出现的次数,键值对最大为O(n), 随后将所有健值对存入一个临时 list, 也花费了O(n)的空间,所以整体空间复杂度为O(2n)

    思路二:HashMap计数 + 堆

    这次仍然是先使用HashMap进行计数,然后维持一个大小为k的最小堆,最后取出堆中所有元素存入一个数组

     1 class Solution {
     2     public int[] topKFrequent(int[] nums, int k) {
     3         // 使用hashmap对每个元素进行出现次数统计
     4         HashMap<Integer, Integer> map = new HashMap<>();
     5         for(int num : nums){
     6             if(map.containsKey(num)){
     7                 map.put(num, map.get(num)+1);
     8             }else{
     9                 map.put(num, 1);
    10             }
    11         }
    12 
    13         // 然后维持一个大小为k的最小堆
    14         PriorityQueue<Integer> queue = new PriorityQueue<>((a, b) -> map.get(a) - map.get(b));
    15         for(Integer key : map.keySet()){
    16             queue.offer(key);
    17             if(queue.size() > k){
    18                 queue.poll();
    19             }
    20         }
    21 
    22         // 最后取出堆中所有元素存入一个数组
    23         int[] res = new int[k];
    24         for(int i = 0; i < k; i++){
    25             res[i] = queue.poll();
    26         }
    27         return res;
    28     }
    29 }

    leetcode 执行用时:18 ms > 45.17%, 内存消耗:41.2 MB > 90.21%

    复杂度分析:

    时间复杂度: O(nlogk)。hashMap计数的时间是O(n), 后面维持一个大小为k的堆的时间花费为O(nlogk), 所以整体时间复杂度为O(nlogk)。

    空间复杂度:O(n+k)。需要一个最大为O(n)的map 和一个 O(k)的堆,所以时间复杂度O(n+k)。

    思路二:HashMap计数 + 桶排序

    先使用hashmap进行计数,然后创建一个大小为 (n + 1) 的列表 list(之所以是n+1, 因为如果所有元素都是同一个元素的话,那这个元素的出现次数就是n, 那下标就是n, 所以需要一个大小为n+1的列表,这个列表的每个元素被当成是一个桶,每个桶是一个小列表,hashmap的值作为下标,键作为桶里的值,这样这个 list 列表中就保存了每个出现次数的所有元素。最后从大小遍历这个list, 把不为空的子列表元素都加入到数组中

     1 class Solution {
     2     public int[] topKFrequent(int[] nums, int k) {
     3         // 使用hashmap对每个元素进行出现次数统计
     4         HashMap<Integer, Integer> map = new HashMap<>();
     5         for(int num : nums){
     6             if(map.containsKey(num)){
     7                 map.put(num, map.get(num)+1);
     8             }else{
     9                 map.put(num, 1);
    10             }
    11         }
    12 
    13         // 然后创建一个大小为 (n + 1) 的列表 list
    14         // 这个列表的每个元素被当成是一个桶,每个桶是一个小列表,hashmap的值作为下标,键作为桶里的值,
    15         // 这样这个 list 列表中就保存了每个出现次数的所有元素。
    16         List<Integer>[] list = new List[nums.length+1];
    17         for(Map.Entry<Integer, Integer> en : map.entrySet()){
    18             if(list[en.getValue()]== null){
    19                 list[en.getValue()] = new ArrayList<Integer>();
    20             }
    21             list[en.getValue()].add(en.getKey());
    22         }
    23 
    24         // 最后从大小遍历这个list, 把不为空的子列表元素都加入到数组中
    25 
    26         ArrayList<Integer> res = new ArrayList<>();
    27         for(int i = nums.length; i >= 0 && res.size() < k; i--){
    28             if(list[i] != null){
    29                 res.addAll(list[i]);
    30             }
    31         }
    32         int[] arr = new int[k];
    33         for(int i = 0; i < k; i++){
    34             arr[i] = res.get(i);
    35         }
    36         return arr;
    37     }
    38 }
    leetcode 执行用时:14 ms > 92.28%, 内存消耗:41.3 MB > 72.55%

    复杂度分析:

    时间复杂度:O(n). 三个for循环,所以时间复杂度为O(n)
    空间复杂度:需要一个大小为O(n)的hashmap, 一个大小为O(n+1)的list,数组,和一个大小为O(k)的结果列表,所以时间复杂度为O(2n+k+1), 即O(n)
  • 相关阅读:
    .net 之邮件发送帮助类 MailKit 的使用
    .NET6 开源之JSON 2 SQL (JORM框架)
    批量上传依赖到离线nexus仓库(maven/npm)
    利用gitblit创建本地Git仓库
    [记录]Git 服务器怎么避免反复密码输入
    配置文件
    spring_IoC底层原理
    Spring_AOP
    事务
    java程序日志
  • 原文地址:https://www.cnblogs.com/hi3254014978/p/13796567.html
Copyright © 2020-2023  润新知