[C++][Leetcode][TopK]前K大问题+前K高频(堆+map)

本文介绍了在C++中利用堆和map解决LeetCode中关于找到数组和数据流中前K大元素及前K高频元素的问题,包括215. 数组中的第K个最大元素、703. 数据流中的第K大元素、面试题 17.14. 最小K个数和347. 前 K 个高频元素。讨论了快速排序、优先队列(最小堆)等算法的应用,并给出了相关代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前两天面试的时候,面试官问了前K大的问题:先是找前K大数,其次是前K高频数。按照面试官的思路一道一道展开~

对总体过程中思考的知识点进行一个总结:

  • 为什么快排常用?
    • 在大规模数据的时候,快速排序只会线性增长,而堆排序增加幅度很大,会远远大于线性。
    • 堆排序指针寻址会耗费很多时间,但是快速排序的话只是移动到前后位置。
    • 参考博文为什么快排最好
  • 介绍一下C++构造函数?
    • 构造函数没有返回值
    • 构造函数用作初始化
    • 构造函数可以重载
  • 点操作符和箭头操作符的区别?
    • 点操作符的对象是实体
    • 箭头操作符的对象是指针
  • C++ 优先队列 堆的使用方式?如何自定义?
  • 为什么sort函数自定义cmp,在类里必须加static关键字?
    • sort中的比较函数compare要声明为静态成员函数或全局函数,不能作为普通成员函数。非静态成员函数是依赖于具体对象的,而std::sort这类函数是全局的,无法再sort中调用非静态成员函数。静态成员函数或者全局函数是不依赖于具体对象的, 可以独立访问,无须创建任何对象实例就可以访问。同时静态成员函数不可以调用类的非静态成员。参考博文

 

目录

1.  Leetcode 215. 数组中的第K个最大元素

2. Leetcode 703. 数据流中的第K大元素

3. Leetcode 面试题 17.14. 最小K个数

4. Leetcode 347. 前 K 个高频元素

 


1.  Leetcode 215. 数组中的第K个最大元素

  • 题目描述

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:

你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/kth-largest-element-in-an-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 思路分析

这个问题有一种非常常见的思路,就是排序,然后取第K个元素即可。最好的时间复杂度是nlogn。但是这个思路,显然不能让面试官满意。说到排序,我们常用的就是快速排序。排序排序有一个partion函数,设定一个哨兵,每次把一个数组分成两部分,前部分小于哨兵元素(或者大于),后部分大于哨兵元素(或者小于)。这样每次调用一次partion函数就可以将一个元素放在其应该在的位置上。我们要求取的是第K大的元素,我们并不关心前k-1个元素是否有序。所以我们可以采用partion函数来寻找第K大的元素。按照通常的解法,我们选取第一个元素作为哨兵。经过一轮partion,如果当前元素的下标小于k-1,表示我们要找的第K大的元素在当前元素的后半部分,否则反之。这样我们最多调用K次partion就可以找到第K大的元素,时间复杂度为Klogn.

  • 代码设计
class Solution {
public:
    void partion(vector<int>&nums,int left,int right,int k)
    {
        if(left<right)
        {
            int privot=nums[left];
            int i&
<think>好的,用户想找LeetCode热门100题的C++解决方案。我需要先确定用户的具体需求,是不是需要具体的题目解答,比如算法思路、代码示例或者优化方法。用户提到的引用内容中有几个代码示例,比如二进制求和、数组旋转、子数组和能被K整除等,这些可能是top100中的题目。 首先,我应该整理LeetCode top100题中常见的题目类型,比如数组、字符串、动态规划、树、链表等。然后,根据用户提供的引用内容,看看是否有对应的题目可以举例。例如,引用1中的二进制求和是LeetCode第67题,属于字符串处理;引用2中的数组旋转是第189题,涉及数组操作;引用3的子数组和能被K整除是第974题,用到了缀和和哈希表;引用4可能涉及图的最短路径问题,比如第743题。 接下来,我需要确保提供的代码示例符合用户要求,使用C++编写,并且是top100中的题目。可能需要确认每个引用对应的题目是否在top100列表中,但根据常见情况,像两数之和、反转链表、二叉树遍历等都属于高频题。 用户可能需要每个题目的简要思路和代码示例,同时生成相关问题。例如,对于快速排序,用户示例中提供了代码和相关问题,这里需要类似的结构。需要注意的是,用户要求回答结构清晰,逐步解决问题,所以需要分步骤讲解每个题目的解决方法,并附上代码。 另外,引用内容中的代码需要注意格式是否正确,比如引用1中的二进制求和代码是否有语法错误,是否需要调整。例如,代码中的循环条件和字符串操作是否正确,是否有转义字符的问题。不过用户提供的代码可能存在一些HTML转义字符,比如<和>,需要转换为<和>,确保代码可读性。 最后,生成相关问题时要围绕每个题目的常见疑问,比如时间复杂度、优化方法、应用场景等。例如,对于数组旋转问题,可以问如何在不使用额外空间的情况下解决,或者如何优化反转方法的性能。 总结步骤: 1. 确定LeetCode top100中的常见题目,并匹配用户提供的引用中的例子。 2. 为每个例子提供问题描述、解决思路、代码示例,并确保代码正确。 3. 生成每个题目相关的后续问题,帮助用户深入理解。 4. 检查引用标记是否正确,代码格式是否符合要求,避免语法错误。 5. 使用中文回答,保持结构清晰,分点说明。</think>以下整理了LeetCode热门100题中部分经典问题C++解决方案,涵盖关键思路与代码实现: --- ### 1. 两数之和(Two Sum) **问题描述**:给定数组和一个目标值,找出数组中两数之和等于目标值的索引。 **解决思路**:利用哈希表记录遍历过的数值及其索引,实现$O(1)$时间复杂度的查找[^1]。 ```cpp #include <vector> #include <unordered_map> using namespace std; class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { unordered_map<int, int> num_map; for (int i = 0; i < nums.size(); ++i) { int complement = target - nums[i]; if (num_map.find(complement) != num_map.end()) { return {num_map[complement], i}; } num_map[nums[i]] = i; } return {}; } }; ``` --- ### 2. 反转链表(Reverse Linked List) **问题描述**:将单链表反转。 **解决思路**:使用三个指针(驱、当、后继),逐步反转节点指向。 ```cpp struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(nullptr) {} }; class Solution { public: ListNode* reverseList(ListNode* head) { ListNode *prev = nullptr, *curr = head; while (curr) { ListNode *next = curr->next; curr->next = prev; prev = curr; curr = next; } return prev; } }; ``` --- ### 3. 二叉树的中序遍历(Binary Tree Inorder Traversal) **问题描述**:非递归实现二叉树的中序遍历。 **解决思路**:使用栈模拟递归过程,优先访问左子树[^2]。 ```cpp #include <vector> #include <stack> using namespace std; struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; class Solution { public: vector<int> inorderTraversal(TreeNode* root) { vector<int> res; stack<TreeNode*> stk; TreeNode *curr = root; while (curr || !stk.empty()) { while (curr) { stk.push(curr); curr = curr->left; } curr = stk.top(); stk.pop(); res.push_back(curr->val); curr = curr->right; } return res; } }; ``` --- ### 4. 子数组和能被K整除(Subarray Sums Divisible by K) **问题描述**:统计数组中连续子数组的和能被K整除的数量。 **解决思路**:缀和+哈希表记录余数频率,时间复杂度$O(n)$[^3]。 ```cpp #include <vector> #include <unordered_map> using namespace std; class Solution { public: int subarraysDivByK(vector<int>& nums, int k) { unordered_map<int, int> map = {{0, 1}}; int ans = 0, sum = 0; for (auto &num : nums) { sum += num; int mod = (sum % k + k) % k; // 处理负数取模 if (map.count(mod)) ans += map[mod]; map[mod]++; } return ans; } }; ``` --- §§ 1. 如何优化两数之和问题以处理数据量? 2. 反转链表的递归实现方法是什么? 3. 二叉树序和后序遍历的非递归实现有何区别? 4. 如何证明缀和+哈希表方法在子数组问题中的正确性? --- **引用说明** : 哈希表优化查找时间的思路常用于数组和字符串问题,如两数之和和二进制求和。 [^2]: 栈和队列是模拟递归和层次遍历的核心数据结构。 : 缀和与哈希表结合可高效解决子数组和问题,如LeetCode 974和560题。 [^4]: 最短路径算法(如Dijkstra)中更新距离数组的方法与贪心策略相关。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值