347. 前 K 个高频元素

本文解析了如何通过HashMap统计元素频率,利用PriorityQueue实现小顶堆对频率进行排序,从而找到出现频率前K的元素。重点介绍了getOrDefault方法的应用和小顶堆在Top K问题中的选择策略。
摘要由CSDN通过智能技术生成
347. 前 K 个高频元素

题目:给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
在这里插入图片描述

这道题之前看了好几遍,不太了解的接口太多,一直就放下了,今天终于把它搞定了!

补充知识:

1.getOrDefault()方法的使用

1.getOrDefault(key,default)

作用:如果存在相应的key则返回其对应的value,否则返回给定的默认值。

2.key的值相同,使value的值加一。比如需要统计一个字符串中所含的字母及对应字母的个数。

方法:

(1)定义一个哈希表hash

(2)hash.put(c,hash.getOrDefault(c,0)+1); //若没有就是0,若有就是原有值增一。

3.比较两个哈希表中key对应的value值大小:如果其中一个哈希表中不存在某个key,那么两个hash表如何比较。

方法:

hash1.getOrDefault(c,0) < hash2.getOrDefault(c,0) //如果两个哈希表都有某个key值,那么他们比较的就是其中value的大小,若其中一个哈希表不存在某个key

值,那么比较的就是其中的默认值。

2.Map的一些用法

**keySet()**方法返回值是Map中key值的集合;**entrySet()**的返回值也是返回一个Set集合,此集合的类型为Map.Entry。

Map.Entry是Map声明的一个内部接口,此接口为泛型,定义为Entry<K,V>。它表示Map中的一个实体(一个key-value对)。接口中有getKey(),getValue方法。

3.PriorityQueue优先级队列用法

用法

先说结论,JAVA中默认是小根堆,即小的在堆顶(poll时小的出去)

默认的最小堆写法

PriorityQueue<Integer> queue = new PriorityQueue<Integer>(new Comparator<Integer>(){
 @Override
 public int compare(Integer o1, Integer o2){
 return o1 < o2 ? -1 : 1; // 最小优先队列,直接 return o1.compareTo(o2);
                          // 最大优先队列,则反过来 return o2.compareTo(o1);
 }
});

compare含义

compare(Integer o1, Integer o2)

一定要记住o1是待插元素,o2是最后一个非叶子节点,当compare()返回的值<0时入队,>0时不入队

小根堆:o1若要入队,则o1<o2,同时compare需要返回负的,即o1.compareTo(o2)

大根堆:o1若要入队,则o1>o2,同时compare需要返回负的,即o2.compareTo(o1)

lambda表达式写法

大括号

PriorityQueue<Integer> queue = 
new PriorityQueue<Integer>((Integer o1, Integer o2)->{
 return o1 < o2 ? -1 : 1; 
 });

省略大括号

PriorityQueue<Integer> queue = 
new PriorityQueue<Integer>((Integer o1, Integer o2)-> o1 - o2);

省略变量类型

PriorityQueue<Integer> queue = new PriorityQueue<Integer>((o1,  o2)-> o1 - o2);

具体场景

TopK问题:

一、找海量数据中最大的K个(当前这道题)

构造小根堆,堆顶为最小数,不断入队,当队列数大于K时,将最小的堆顶弹出,最后剩下的就是K个最大数

二、数组中最小的K个

构造最大堆,堆顶为最大数,不断入队,当队列数小于K时,将最大的堆顶弹出,最后剩下的就是K个最小数

思路(来自 代码随想录)

这道题目主要涉及到如下三块内容:

  1. 要统计元素出现频率
  2. 对频率排序
  3. 找出前K个高频元素

首先统计元素出现的频率,这一类的问题可以使用map来进行统计。

然后是对频率进行排序,这里我们可以使用一种 容器适配器就是优先级队列

什么是优先级队列呢?

其实就是一个披着队列外衣的堆,因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。

而且优先级队列内部元素是自动依照元素的权值排列。那么它是如何有序排列的呢?

堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。

所以大家经常说的大顶堆(堆头是最大元素),小顶堆(堆头是最小元素),如果懒得自己实现的话,就直接用priority_queue(优先级队列)就可以了,底层实

现都是一样的,从小到大排就是小顶堆,从大到小排就是大顶堆。

本题我们就要使用优先级队列来对部分频率进行排序。

此时要思考一下,是使用小顶堆呢,还是大顶堆?

有的同学一想,题目要求前 K 个高频元素,那么果断用大顶堆啊。

那么问题来了,定义一个大小为k的大顶堆,在每次移动更新大顶堆的时候,每次弹出都把最大的元素弹出去了,那么怎么保留下来前K个高频元素呢。

而且使用大顶堆就要把所有元素都进行排序,那能不能只排序k个元素呢?

所以我们要用小顶堆,因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。

题解

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        int[] result = new int[k];
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }

        Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
        // 根据map的value值正序排,相当于一个小顶堆
        PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>((o1, o2) -> o1.getValue() - o2.getValue());
        for (Map.Entry<Integer, Integer> entry : entries) {
            queue.offer(entry);
            if (queue.size() > k) {
                queue.poll();
            }
        }
        for (int i = k - 1; i >= 0; i--) {
            result[i] = queue.poll().getKey();
        }
        return result;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值