求数组中最小的k个数以及海量数据最大堆、multiset解决方案


【题目】

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。


【方案一】

主要有两种方案。第一是利用我们熟知的 partition 算法,它是快速排序的核心,相信每个人都会。它可以用来求取数组的任意第 k 大的数,时间复杂度是O(n)。我们不断对数据 partition,当它返回的 index 为第 k-1 是,那么就说明前 k 个数(包括 index对应的数)就是最小的 k 个数了。因为 index 对应数的左侧都比它小,一共 0~k-2 即 k-1 个,加上它自己,就是 k 个了。


代码:

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        const int size = input.size();
        vector<int> res;
        if(size == 0 || k <= 0 || k > size)
            return res;
        if(k == size)
            return input;
    
        int start = 0;
        int end = size - 1;
        int index = partition(input, start, end);
        while(index != k - 1){ 
           
<think>嗯,用户让我用multiset写一个子函数,输入是一组整数,然后打印最大的k个数。而且要写得新手一些。首先,我得理解用户的需。他们可能刚开始学C++,对STL容器还不太熟悉,所以需要详细解释每一步。 首先,multiset是什么?multiset是C++ STL中的一个容器,允许重复元素,并且自动排序。默认情况下,multiset是按升序排列的。那要取最大的k个数,可能需要逆序遍历,或者改变排序方式。不过,默认是升序,所以最大的数在末尾。 那如何用multiset来实现呢?可能的步骤是:将输入的整数存入multiset,然后从末尾开始取k个元素。但要注意,如果k比multiset的大小还大,就要全部输出。 不过,multiset的插入操作是O(log n)时间,总体构建是O(n log n)。对于大数据量可能不够高效,但用户可能只是需要一种简单的方法,所以可以接受。 接下来,函数参数应该怎么设计?用户说输入是一组整数,可能是指一个vector或者数组。考虑到新手友好,使用vector<int>作为输入参数比较合适。然后是整数k。 函数内部,首先把vector里的所有元素插入multiset。然后,需要从最大的元素开始取k个。因为multiset是排序的,所以最大的元素在rbegin()的位置。 但是,multiset允许重复,所以直接取最后k个元素就行。可以用反向迭代器来遍历,从rbegin()到rend(),然后计数,直到取满k个。 需要注意的情况是,当k为0或者负数时,应该处理错误情况。还有当k大于multiset的大小时,就输出所有元素。 代码结构大致如下: void printTopK(const vector<int>& nums, int k) { multiset<int> ms; for(int num : nums) { ms.insert(num); } // 然后反向遍历,取k个 int count = 0; for(auto it = ms.rbegin(); it != ms.rend() && count < k; ++it) { cout << *it << " "; count++; } } 但这样可能有问题,因为multiset默认升序,所以rbegin()是最大的元素。这样应该是对的。 不过,有没有更高效的方式?比如使用greater<int>作为比较函数,让multiset按降序排列,这样直接从头开始取k个。但这对新手来说可能有点复杂,但可以解释清楚。 例如: multiset<int, greater<int>> ms; 这样插入时会按降序排列,然后直接从头开始取k个。 但两种方法都可以,可能后者更直观,因为直接取前k个元素,不需要反向迭代器。不过,反向迭代器对于新手可能有点难理解,所以可能需要详细注释。 另外,用户要函数是子函数,也就是一个独立的函数,可能在main中调用。所以需要包含必要的头文件,比如<iostream>, <set>, <vector>等。 还需要处理k的有效性,比如k<=0时给出提示,或者k超过元素个数时处理。 在代码中,可能需要添加判断: if (k <= 0) { cout << "k必须大于0" << endl; return; } k = min(k, (int)ms.size()); // 防止k过大 这样更安全。 综合起来,代码可能如下: #include <iostream> #include <set> #include <vector> using namespace std; void printTopK(const vector<int>& nums, int k) { if (k <= 0) { cout << "错误:k必须大于0" << endl; return; } multiset<int, greater<int>> ms; // 降序排列 for(int num : nums) { ms.insert(num); } if (ms.empty()) { cout << "数组为空!" << endl; return; } k = min(k, (int)ms.size()); // 实际最多取全部元素 cout << "最大的" << k << "个数是:"; int count = 0; for(auto it = ms.begin(); count < k; ++it, ++count) { cout << *it << " "; } cout << endl; } int main() { vector<int> test = {3, 1, 4, 1, 5, 9, 2, 6}; printTopK(test, 3); return 0; } 这样,multiset使用greater<int>来降序排列,直接从头开始取k个。对于新手来说,可能需要解释为什么使用greater<int>,以及multiset的模板参数。 在解释时,需要详细说明每一步的作用,比如multiset的声明、插入元素的过程,以及如何遍历。此外,还要提到处理k的有效性,以及可能出现的边界情况,比如空输入、k过大等。 用户可能还希望看到示例输出,比如输入{3,1,4,1,5,9,2,6},k=3时,输出9,6,5。但multiset插入后是{1,1,2,3,4,5,6,9},按降序排列的话是9,6,5,4,3,2,1,1。所以最大的3个是9,6,5。对,这样是正确的。 另外,multiset允许重复,所以如果有重复的最大值,比如输入多个9,都会正确输出。 总结,代码需要包括必要的错误处理,清晰的注释,以及对新手友好的解释,比如为什么使用greater<int>,反向迭代器的作用,或者如何调整multiset的排序方式。</think>以下是用`multiset`实现打印最大k个数的完整代码及详细解释,特别为新手设计: ```cpp #include <iostream> #include <set> // 包含multiset头文件 #include <vector> // 使用vector存储输入数组 using namespace std; // 函数功能:打印数组中最大的k个数 // 参数说明:nums-整数数组,k-要输出的最大数的个数 void printTopK(const vector<int>& nums, int k) { // 错误检查:k不能小于等于0 if (k <= 0) { cout << "错误:k必须大于0" << endl; return; } // 创建按从大到小排序的multiset // greater<int>表示使用"大于"比较器,让元素按降序排列 multiset<int, greater<int>> sortedNumbers; // 将数组元素插入multiset(自动排序) for(int num : nums) { sortedNumbers.insert(num); } // 处理空数组的情况 if (sortedNumbers.empty()) { cout << "警告:输入的数组为空!" << endl; return; } // 实际最多取全部元素(当k超过元素个数时) k = min(k, (int)sortedNumbers.size()); // 打印结果 cout << "最大的" << k << "个数是:"; int count = 0; // 遍历已排序的multiset(此时已经是从大到小排列) for(auto it = sortedNumbers.begin(); count < k; ++it, ++count) { cout << *it << " "; } cout << endl; } int main() { // 测试用例 vector<int> numbers = {3, 1, 4, 1, 5, 9, 2, 6}; // 正常情况测试 cout << "测试1(k=3):" << endl; printTopK(numbers, 3); // 应输出9 6 5 // k超过数组长度测试 cout << "\n测试2(k=10):" << endl; printTopK(numbers, 10); // 应输出所有元素 // 错误参数测试 cout << "\n测试3(k=-5):" << endl; printTopK(numbers, -5); // 应显示错误信息 return 0; } ``` ### 🎯 代码图解 ```plaintext 执行流程: 输入数组: [3,1,4,1,5,9,2,6] ↓ 插入multiset(降序排列) multiset内容:9,6,5,4,3,2,1,1 ↓ 取前k个元素(k=3) 输出结果:9 6 5 ``` ### 🌟 关键知识点(新手必看) 1. **multiset特性**: - 允许存储重复元素 - 自动排序(默认升序,通过`greater<int>`改为降序) - 插入操作时间复杂度:O(log n) 2. **比较器`greater<int>`的作用**: ```cpp // 常规multiset(升序排列) multiset<int> normalSet; // 存储顺序:1,2,3,4... // 使用比较器的multiset(降序排列) multiset<int, greater<int>> descSet; // 存储顺序:9,8,7,6... ``` 3. **为什么不用反向迭代器?** - 虽然可以用`rbegin()`从末尾反向遍历,但通过改变排序方式更直观 - 对比两种方式: ```cpp // 方式一:默认升序 + 反向迭代器 multiset<int> ms; // 1,2,3 for(auto rit = ms.rbegin(); ...) // 3,2,1 // 方式二:降序排列 + 正向迭代器(更直观) multiset<int, greater<int>> ms; // 3,2,1 for(auto it = ms.begin(); ...) // 3,2,1 ``` ### 💡 常见问题解答 **Q1:如果数组中有重复元素怎么办?** - 这正是`multiset`的优势,例如输入`[5,5,3]`,当k=2时会正确输出`5,5` **Q2:时间复杂度太高怎么办?** - 该实现的时间复杂度是O(n log n),适用于小数据量 - 如果处理大数据,可以使用优先队列(priority_queue)将复杂度优化到O(n log k) **Q3:如何修改为从小到大排序?** - 只需修改声明部分: ```cpp multiset<int> sortedNumbers; // 默认升序排列 // 取最大k个需要从末尾开始取 auto it = sortedNumbers.rbegin(); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值