栈与队列——7.力扣题目:347. 前 K 个高频元素

题目链接

解析:(小顶堆)

题目要求:给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

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

大体思路:

​ ①题目涉及了出现次数,首先想到使用map进行存储次数。

​ ②题目需要找前k个最大值,首先想到使用小顶堆来做

具体步骤:

​ ①定义一个map,key存储数组元素,value存储元素出现的次数。

​ ②定义一个小顶堆,用于寻找k个最大的元素,存放数组{元素,出现次数}。小顶堆里的存储顺序是从队头到队尾是从小到大的。这个堆的排列顺序是按照出现次数进行排序的。

​ ③初始化堆,添加入map中前k个元素。遍历map是使用entrySet()返回含有键值对entry对象的集合,再增强for遍历这个集合。添加{entry.getKey(), entry.getValue()}到小顶堆中。
​ ④向后遍历,每次添加元素前,比较元素是否大于队头存放的int数组的第1个值(也就是堆顶(队头)元素,对应的是堆中的最小值),大于队头元素,则弹出队头元素后再添加{entry.getKey(), entry.getValue()}进入堆。这样可确保堆顶的是大于堆外其他元素的,因为除非堆外元素大于堆顶,否则不可以被添加进来。

注意点:(堆和Map的基础知识)

注意点①:

java中使用优先级队列PriorityQueue实现堆:

​ 从队头到队尾按从小到大排就是最小堆(小顶堆),队头是最小的元素

​ 从队头到队尾按从大到小排就是最大堆(大顶堆),队头是最大的元素

示例:

// 使用优先级队列实现小顶堆,优先级队列里存放了一个一个的int[]数组,并且按照数组的第1位的大小关系,从队头到队尾由小到大进行排序
// lambda表达式i-j是小的排前面
PriorityQueue<int[]> pq = new PriorityQueue<>((i, j) -> i[1] - j[1]);

注意点②:

Map.Entry 键值对 (遍历map数组的方法)

Map.entrySet()会返回一个集合set,这个set集合里每一个元素的类型是Map.Entry<K,V>

Map.Entry<K,V>表示Map中的一个键值对(一个key-value对),这也是一个类,只是Entry是Map中的静态内部类。
Map.Entry<K,V>类可以通过getKey(), getValue()进行获取键和值

所以可以使用Entry来遍历Map数组:

for (Map.Entry<Integer, Integer> entry : map.entrySet()){
    System.out.println(entry.getKey());
    System.out.println(entry.getValue())
}

注意点③:

map.getOrDefault(key) 返回map中这个key对应的value,如果没有这个key,返回设定值

这个方法适合用于统计key出现的次数

for (int i : nums) {
    // 实现统计nums数组中相同数字出现的次数
	map.put(i, map.getOrDefault(i, 0) + 1);
}

注意点④:

new int[]{元素1,元素2 …} 不仅在数组初始化时可以用,也可以直接用

比如:set.add(new int[]{1, 2, 3, 4});

代码:
class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<>();
        // 将数组中的元素,按照(值,出现次数)存入Map
        for (int i : nums) {
            map.put(i, map.getOrDefault(i, 0) + 1);
        }
        // 定义优先级队列,存放数组,数组用于存放{值,出现次数},并且按照数组的第1个元素(也就是出现次数)的从队头到队尾按照从小到达进行排序。这样就实现了小顶堆。
        PriorityQueue<int[]> pq = new PriorityQueue<>((i, j) -> i[1] - j[1]);
        int index = 0;
        // 遍历map,使用entrySet(),获取其键值对对象Entry进行遍历
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            // 先存入前k个元素
            if (index < k) {
                pq.add(new int[]{entry.getKey(), entry.getValue()});
                index++;
            } else {
                // 当输入的元素大于堆顶元素,弹出堆顶元素,存入这个元素
                // 因为小顶堆的队头是最小值,每次弹出最小值,加入更大的元素,这样确保堆内最小的元素都大于数组所有其他元素,也就保证了堆是里存放的是前k个最大的元素。
                if (pq.peek()[1] < entry.getValue()) {
                    pq.poll();
                    pq.add(new int[]{entry.getKey(), entry.getValue()});
                }
                // 如果当输入的元素小于堆顶元素,不做任何操作,直接略过这个元素
                
            }
        }
        int[] res = new int[k];
        // 最后输出了出现次数由小到大的序列(题目表示可以按任意顺序返回答案)
        for (int i = 0; i < k; i++) {
            res[i] = pq.poll()[0];
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值