【LeetCode Hot100 堆】第 K 大的元素、前 K 个高频元素

  • 堆(Heap)数据结构
    • [1. 什么是堆?](#1. 什么是堆?)
    • [2. 堆的存储方式](#2. 堆的存储方式)
    • [3. Java 中的堆(PriorityQueue)](#3. Java 中的堆(PriorityQueue))
  • [第 K 大的元素](#第 K 大的元素)
    • 题目描述
    • 示例
    • [使用堆求解第 K 大的元素](#使用堆求解第 K 大的元素)
      • [1. 方法概述](#1. 方法概述)
      • [2. 解题思路](#2. 解题思路)
      • [3. 代码实现](#3. 代码实现)
  • [前 K 个高频元素](#前 K 个高频元素)
    • [1. 题目描述](#1. 题目描述)
    • [2. 解题思路](#2. 解题思路)
    • [3. 代码实现](#3. 代码实现)
      • [使用 `PriorityQueue`(Java 默认最小堆)](#使用 PriorityQueue(Java 默认最小堆))
      • [Map.Entry<K, V> 介绍](#Map.Entry<K, V> 介绍)
      • [entrySet() 作用](#entrySet() 作用)
      • [Comparator.comparingInt(a -> a[1])](#Comparator.comparingInt(a -> a[1]))

堆(Heap)数据结构

1. 什么是堆?

堆(Heap) 是一种 特殊的完全二叉树,满足以下性质:

  • 任意节点的值 都满足 堆的性质(最大堆或最小堆)。
  • 完全二叉树:除了最后一层,所有层都被填满,且最后一层的节点靠左排列。

两种常见类型

  1. 最小堆(Min Heap) :父节点的值 小于等于 其子节点。
    • 堆顶元素 是最小值。
  2. 最大堆(Max Heap) :父节点的值 大于等于 其子节点。
    • 堆顶元素 是最大值。

2. 堆的存储方式

堆通常使用 数组 存储,并且满足:

  • 父节点parent(i) = (i - 1) / 2
  • 左子节点left(i) = 2 * i + 1
  • 右子节点right(i) = 2 * i + 2

3. Java 中的堆(PriorityQueue)

Java 提供了 PriorityQueue 实现最小堆:

java 复制代码
import java.util.PriorityQueue;

PriorityQueue<Integer> minHeap = new PriorityQueue<>(); // 默认最小堆
minHeap.offer(5);
minHeap.offer(2);
minHeap.offer(8);

System.out.println(minHeap.poll()); // 输出 2(最小值)

如果要创建最大堆:

java 复制代码
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);

第 K 大的元素

题目描述

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

注意

  • 你需要找的是数组排序后的 第 k 个最大的元素,而不是第 k 个不同的元素。
  • 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例

输入

java 复制代码
nums = [3,2,1,5,6,4], k = 2

输出

java 复制代码
5

使用堆求解第 K 大的元素

1. 方法概述

使用 最小堆(Min Heap) 可以高效地求解 第 K 大的元素 ,该方法的核心思想是维护一个大小为 k最小堆 ,其中堆顶元素始终是当前 前 K 大元素 中的最小值。


2. 解题思路

  1. 创建一个大小为 k 的最小堆PriorityQueue)。
  2. 遍历数组 nums
    • 如果 堆的大小小于 k ,则直接将当前元素 num 插入堆中。
    • 如果 堆的大小等于 k
      • 只有当 num > 堆顶元素 时,才将堆顶元素移除,并插入 num
  3. 遍历结束后,堆顶元素即为第 k 大的元素

3. 代码实现

java 复制代码
import java.util.PriorityQueue;

class Solution {
    public int findKthLargest(int[] nums, int k) {
        // 创建一个最小堆(容量为 k)
        PriorityQueue<Integer> minHeap = new PriorityQueue<>(k);

        // 遍历数组
        for (int num : nums) {
            if (minHeap.size() < k) { 
                // 直接入堆
                minHeap.offer(num);
            } else if (num > minHeap.peek()) { 
                // 只在当前元素比堆顶元素大的时候进行替换
                minHeap.poll();
                minHeap.offer(num);
            }
        }
        // 堆顶即为第 k 大的元素
        return minHeap.peek();
    }
}

前 K 个高频元素

1. 题目描述

给定一个整数数组 nums 和一个整数 k,要求找出 出现频率最高的 K 个元素 。返回顺序可以 任意


2. 解题思路

  1. 使用 HashMap 统计元素频率:遍历数组,记录每个数字的出现次数。
  2. 构建最小堆 (小根堆):维护大小为 K 的最小堆 ,堆顶存储当前出现频率最低的元素
  3. 遍历 HashMap 并更新堆
    • 若堆的大小小于 k,则直接加入堆。
    • 若新元素的频率 大于堆顶元素的频率,则替换堆顶元素并调整堆。
  4. 堆中存储的即是前 K 个高频元素

3. 代码实现

使用 PriorityQueue(Java 默认最小堆)

java 复制代码
import java.util.*;

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        // 1. 统计频率
        Map<Integer, Integer> freqMap = new HashMap<>();
        for (int num : nums) {
            freqMap.put(num, freqMap.getOrDefault(num, 0) + 1);
        }

        // 2. 使用最小堆存储前 K 个高频元素
        PriorityQueue<int[]> minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a[1]));

        for (Map.Entry<Integer, Integer> entry : freqMap.entrySet()) {
            int num = entry.getKey(), freq = entry.getValue();
            if (minHeap.size() < k) {
                minHeap.offer(new int[]{num, freq});
            } else if (freq > minHeap.peek()[1]) {
                minHeap.poll(); // 移除频率最小的
                minHeap.offer(new int[]{num, freq});
            }
        }

        // 3. 取出堆中的元素
        int[] result = new int[k];
        for (int i = 0; i < k; i++) {
            result[i] = minHeap.poll()[0];
        }
        return result;
    }
}

Map.Entry<K, V> 介绍

  • Map.Entry<K, V>Java Map 接口的内部接口 ,表示 Map 中的 键值对(key-value)
  • entrySet() 方法返回 包含所有键值对 (Entry) 的集合 ,然后可以用 for-each 遍历。

entrySet() 作用

freqMap.entrySet() 返回一个 Set<Map.Entry<K, V>>,其中:

  • 每个 Entry<K, V> 代表 一个键值对
  • getKey() 获取键(数字)。
  • getValue() 获取值(出现的次数)。

Comparator.comparingInt(a -> a[1])

在 Java 中,Comparator.comparingInt(a -> a[1]) 用于创建一个 基于数组元素的某个索引值进行排序 的比较器,主要用于 PriorityQueue(优先队列) 或 Collections.sort()。
Comparator.comparingInt(a -> a[1])

  • comparingInt(ToIntFunction<T> keyExtractor):Comparator 提供的 辅助方法,用于按照 int 类型的键排序。
  • a -> a[1]Lambda 表达式 ,指定对 a 这个数组的 索引 1 位置的值进行排序。
相关推荐
_不会dp不改名_27 分钟前
leetcode_80删除有序数组中的重复项 II
数据结构·算法·leetcode
√尖尖角↑27 分钟前
力扣——【104. 二叉树的最大深度】
python·算法·leetcode
幸运小圣28 分钟前
LeetCode热题100- 轮转数组【JavaScript讲解】
javascript·算法·leetcode
MYX_30928 分钟前
第七节 文件与流
开发语言·c++·学习·算法
Lostgreen34 分钟前
解密K-means:简单易懂的算法指南
算法·机器学习·kmeans
muxue1782 小时前
数据结构:栈
java·开发语言·数据结构
惊岁晚2 小时前
【R】Dijkstra算法求最短路径
算法·贪心算法·r语言
隼玉6 小时前
C语言简单练习题
c语言·c++·笔记·学习·算法
牛奶咖啡.8549 小时前
经典排序算法复习----C语言
c语言·开发语言·数据结构·算法·排序算法
d-jero9 小时前
cpp之模板
开发语言·c++·算法