LeetCode 热题 100 刷题笔记:高频面试题详解(215 & 347)

在算法面试中,数组与哈希表 ​ 类题目是绝对的高频考点。今天我们通过两道经典题目 ------ 215. 数组中的第K个最大元素 ​ 和 347. 前 K 个高频元素,来深入剖析其解题思路、算法选型与时间空间复杂度的权衡。


一、215. 数组中的第K个最大元素

📌 题目描述

给定整数数组 nums和整数 k,返回数组中第 k个最大的元素。(注意:是第 k个最大,不是第 k个不同元素)

示例:

  • 输入:[3,2,1,5,6,4], k = 2→ 输出:5

  • 输入:[3,2,3,1,2,4,5,5,6], k = 4→ 输出:4


🧠 解题思路

题目看似简单,但关键在于如何高效找到"第 K 大"元素。常见解法有三种:

✅ 方法一:最小堆(图片中代码所用)
  • 思路 :维护一个大小为 k的最小堆,堆顶始终是当前堆中最小的元素(即第 k大的候选)。

  • 过程

    1. 遍历数组,将每个元素压入堆。

    2. 若堆大小 > k,弹出堆顶(丢弃当前最小的)。

    3. 最终堆顶即为第 k大元素。

  • 时间复杂度:O(n log k)

  • 空间复杂度:O(k)

💡 优点:实现简单,适合大数据流或部分排序场景。

⚠️ 缺点:未达到题目提示的 O(n) 最优复杂度。

🚀 方法二:快速选择(Quick Select)------ 满足 O(n) 要求
  • 思路:基于快速排序的分区思想,每次将数组划分为"大于 pivot"和"小于 pivot"两部分,根据 pivot 的位置决定递归哪一侧。

  • 平均时间复杂度:O(n)

  • 最坏情况:O(n²)(可通过随机化 pivot 优化)

📦 方法三:排序法(暴力解)
  • 直接排序后取 nums[n-k],时间复杂度 O(n log n),不推荐用于面试。

💻 图片中代码解析(C++)

复制代码
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int, vector<int>, greater<int>> minHeap;
        for(int num:nums) {
            minHeap.push(num);
            if(minHeap.size() > k)
                minHeap.pop();
        }
        return minHeap.top();
    }
};

优点

  • 逻辑清晰,易于理解。

  • 适合处理流式数据或内存受限场景。

缺点

  • 未满足题目"O(n) 时间复杂度"的要求(虽然 LeetCode 通常接受堆解法,但面试中可能被追问更优解)。

二、347. 前 K 个高频元素

📌 题目描述

给定一个整数数组 nums和整数 k,返回出现频率前 k高的元素(顺序可任意)。

示例:

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

  • 输入:[1], k = 1→ 输出:[1]


🧠 解题思路

核心是:统计频率 + 取前 K 个高频

✅ 方法一:哈希表 + 最小堆(图片中代码所用)
  • 步骤

    1. unordered_map统计每个元素的频率。

    2. 用最小堆维护前 k个高频元素(堆顶是频率最小的)。

    3. 遍历频率表,若堆大小 < k,直接入堆;否则,若当前元素频率 > 堆顶频率,则替换。

    4. 最后堆中元素即为答案。

  • 时间复杂度:O(n log k)

  • 空间复杂度:O(n)

💡 优点:稳定、易懂,适合大多数面试场景。

⚠️ 注意:题目要求"任意顺序",所以不需要对结果排序。

🚀 方法二:桶排序(Bucket Sort)------ 更优解法
  • 思路

    1. 统计频率 → 得到频率最大值 maxFreq

    2. 创建大小为 maxFreq + 1的桶,桶索引 = 频率,桶内存储对应元素。

    3. 从后往前遍历桶,取出元素直到凑够 k个。

  • 时间复杂度:O(n)

  • 空间复杂度:O(n)


💻 图片中代码解析(C++)

复制代码
class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> freqMap;
        for(int num:nums)
            freqMap[num]++;

        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> minHeap;
        for (auto& entry : freqMap) {
            int num = entry.first;
            int freq = entry.second;
            minHeap.push({freq, num});
            if (minHeap.size() > k) {
                minHeap.pop(); // 保持堆大小为 k,弹出频率最小的
            }
        }

        vector<int> result;
        while (!minHeap.empty()) {
            result.push_back(minHeap.top().second);
            minHeap.pop();
        }
        return result;
    }
};

亮点

  • 使用 pair<int, int>存储频率和元素,便于堆比较。

  • 堆大小控制在 k,避免内存浪费。

  • 最后返回结果时无需排序,符合题目"任意顺序"要求。


📊 总结对比

题目 解法 时间复杂度 空间复杂度 是否推荐面试
215. 第K个最大元素 最小堆 O(n log k) O(k) ✅ 基础解法,必会
215. 第K个最大元素 快速选择 O(n) O(1) ✅ 高阶解法,面试加分项
347. 前K个高频元素 哈希+最小堆 O(n log k) O(n) ✅ 标准解法,必会
347. 前K个高频元素 桶排序 O(n) O(n) ✅ 最优解法,体现算法深度

🎯 面试建议

  1. 先写堆解法:逻辑清晰,面试官容易理解,适合快速通过。

  2. 再提优化:如"如果追求 O(n) 时间,可以用快速选择或桶排序",体现思维深度。

  3. 注意边界 :如 k=1k=n空数组等特殊情况。

  4. 代码规范:变量命名清晰、注释到位、避免硬编码。


📚 延伸阅读

  • 快速选择算法详解

  • 桶排序原理与应用

  • 堆排序与优先队列\]([https://en.wikipedia.org/wiki/Heap_(data_structure)](https://en.wikipedia.org/wiki/Heap_%28data_structure%29 "https://en.wikipedia.org/wiki/Heap_(data_structure)"))

📌 一句话总结

堆是解决"Top K"问题的万能钥匙,但真正的算法高手,懂得在合适场景选择更优的 O(n) 解法。

如果你正在准备算法面试,这两道题是必刷经典,建议亲手实现并对比不同解法的优劣。祝你刷题顺利,offer 拿到手软! 🚀

相关推荐
mmz12072 小时前
贪心算法3(c++)
c++·算法·贪心算法
j_xxx404_2 小时前
蓝桥杯基础--排序模板合集II(快速,归并,桶排序)
数据结构·c++·算法·蓝桥杯·排序算法
月落归舟2 小时前
排序算法---(四)
算法·排序算法
童话ing2 小时前
【LeetCode】239.滑动窗口最大值
数据结构·算法·leetcode·golang
计算机安禾2 小时前
【数据结构与算法】第13篇:栈(三):中缀表达式转后缀表达式及计算
c语言·开发语言·数据结构·c++·算法·链表
章鱼丸-2 小时前
DAY40 训练与测试规范写法
人工智能·算法·机器学习
代码飞天3 小时前
算法与数据结构之又臭又长的表
数据结构·算法
A923A3 小时前
【洛谷刷题 | 第七天】
算法·模拟·洛谷
故事和你913 小时前
洛谷-入门4-数组3
开发语言·数据结构·c++·算法·动态规划·图论