取map容器的前10大(根据每一个value排序)元素

本文介绍了如何从map中找出出现次数最多的10个元素,通过将map中的元素复制到vector,然后利用partial_sort进行排序。讨论了partial_sort的效率、选用vector而非list的原因以及为何不能直接用map的迭代器构造vector。
部署运行你感兴趣的模型镜像

 XX公司的笔试题:

  给定一个仅包含英文字母和空格的字符串,请实现一个函数找出该字符串中出现次数最多的10个字母(不区分大小写)。

      当时的解答是:

#include <iostream>
#include <algorithm>
#include <map>

#include <vector>
using namespace std;

bool compare_map(const pair<char, int> &p1, const pair<char,int> &p2)
{
     return p1.second < p2.second;
}
char* Find_top10Char(const char* str)
{
     if (!str)
         return NULL;
     const char *pStr = str;
     static char res[11] ;

     memset(res, 0, sizeof(res));
     map<char,int> tmp;
     while (*pStr != '/0')
     {
          char tp = *pStr;

          if(*pStr == ' ')

               pStr++,continue;
          if (*pStr + 32 < 122)  //如果是大写,转换成小写
          {
                tp = *pStr + 32;
          }
          tmp[tp]++;
          pStr++;
 }
 map<char,int>::iterator iter = tmp.end(); 
 for (int i = 0; i < 10 && !tmp.empty(); ++i)
 {
      iter = max_element(tmp.begin(), tmp.end(), compare_map);  //效率太低
      if (iter != tmp.end())
      {
          res[i] = iter->first;
          tmp.erase(iter);
          continue;  
      } 
      break;
  } 
      return res;
}

//test
int main()
{
     const char *pp = "aaajjjj jaaaaf fffjjjllllf fflllo ffoaoaaoottotrrttrwrwqwqu";
     char *res = Find_top10Char(pp);
     cout<<res<<endl;
     return 1;
}

  将源字符串中出现过的字符都记录下来,放在map容器的pair中,字符每出现多一次就将该pair的second加1。知道遍历完整个字符串。

  接下来,需要对map容器根据pair的second进行排序,得到前10个元素。可是map容器本身就是已排好序的容器(根据每一个key),因此不能改变该容器的顺序,只能另谋它法了。

  要想得到和map结构一样的非排序容器,可以用vector或list来完成,将pair<char,int>作为它的元素,取出该容器前10大的元素,可用partial_sort()这一函数。

      在此采用vector来实现:

vector<pair<char,int> > vec(tmp.size());   //先预留空间,避免频繁更换内存块

//copy(tmp.begin(), tmp.end(), back_inserter(vec));

int idx = 0;  //在下面定义map迭代器处接着定义idx,老是编译错误,不知为啥

for (map<char, int>::iterator iter = tmp.begin(); iter != tmp.end(); ++iter, ++idx)
{
   vec[idx] = make_pair(iter->first, iter->second);

}
partial_sort(vec.begin(), vec.begin() + 9, vec.end(), compare_map);  //此时应将compare_map函数"改>"

然后再:

for (int i = 0; i < 10 && !vec.empty(); ++i)
{
    pair<char,int> &pr = vec.front();
    res[i] = pr.first;
    vec.erase(vec.begin());
}

 

这里有几个疑问:

1.为什么改用partial_sort之后,效率就高了呢?它的原理是什么?

2.为什么要用vector而不用list呢?假如需要存放的数据很多的话,vector不是低效吗?

3.为什么构造vec对象的时候不采用vector<pair<char,int> > vec(tmp.begin(), tmp.end());直接往vec填元素呢?

答:

1.partial_sort是通过堆排序实现的,具体看“从partial_sort挖掘堆排序”这篇。

2.正是考虑到vector是顺序存储元素的,所以预先分配了足够的空间,然后通过下标即可对其每个元素赋值。

3.这样的用法本身有问题,在模板类vector中的iterator只是一个unsigned int类型,而map或list等模板类中的iterator却是class,当然不能通过编译,起码VC6不行。

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

<think>根据用户查询和提供的引用内容,用户需要了解在C++中如何根据mapvalue值进行排序。由于C++的map默认按照key排序,而无法直接按value排序,因此需要间接方法。引用中提到了几种方法,我将整理并解释这些方法。 主要方法总结如下: 1. **Vector转换法**:将map的键值对复制到vector中,然后使用自定义比较函vector排序。 2. **构造新容器法**:使用multimap(允许重复键)或set,但需要反转键值对(即value作为键,key作为值),但注意value可能重复,所以通常用multimap。 3. **优先队列法**:使用优先级队列(priority_queue)来存储pair,并自定义比较函。 4. **自定义比较器法**:在创建map时尝试自定义比较器,但这种方法实际上无法实现按value排序,因为map在插入时只根据key排序。所以此方法不适用,但引用[2]中提到了,因此我们指出其局限性。 我将详细解释三种方法,并提供代码示例。 注意:引用[4]提供了一个完整的Vector转换法的例子,引用[3]则说明了如何遍历map。 步骤: 1. **Vector转换法**(最通用): - 将map中的每个键值对(pair)放入vector中。 - 使用标准库的sort函,并传入一个自定义的比较函(或lambda表达式)来指定按value比较。 2. **构造新容器法**: - 创建一个新的multimap,将原mapvalue作为新容器的key,原map的key作为新容器value。因为multimap允许重复的key(即原mapvalue可能重复),所以适合。 - 注意:如果原mapvalue不重复,也可以用map,但通常用multimap更安全。 3. **优先队列法**: - 使用priority_queue,并指定比较方式为按value小。priority_queue默认是最大堆,可以通过自定义比较函来改变顺序。 下面分别给出代码示例: ### 1. Vector转换法 ```cpp #include <iostream> #include <map> #include <vector> #include <algorithm> using namespace std; // 自定义比较函,按value升序排列 bool cmp(pair<int, int>& a, pair<int, int>& b) { return a.second < b.second; } int main() { map<int, int> myMap = {{1, 90}, {2, 87}, {3, 92}, {4, 90}}; // 将map中的元素复制到vectorvector<pair<int, int>> vec(myMap.begin(), myMap.end()); // 对vector排序 sort(vec.begin(), vec.end(), cmp); // 输出排序后的结果 for (auto& p : vec) { cout << p.first << ": " << p.second << endl; } return 0; } ``` ### 2. 构造新容器法(使用multimap) ```cpp #include <iostream> #include <map> using namespace std; int main() { map<int, int> myMap = {{1, 90}, {2, 87}, {3, 92}, {4, 90}}; // 创建一个multimap,将原mapvalue作为key,原map的key作为value multimap<int, int> sortedMap; for (auto& kv : myMap) { sortedMap.insert({kv.second, kv.first}); // 注意:这里键值对调 } // 遍历multimap(multimap默认按key升序排列) for (auto& kv : sortedMap) { cout << kv.second << ": " << kv.first << endl; } return 0; } ``` ### 3. 优先队列法(使用最大堆按value降序) 如果希望按value降序排列(从到小),可以使用最大堆。若想升序,则使用最小堆(通过greater或自定义比较函实现)。 ```cpp #include <iostream> #include <map> #include <queue> // 包含priority_queue #include <vector> using namespace std; // 自定义比较函,用于优先队列(最大堆) struct Compare { bool operator()(const pair<int, int>& a, const pair<int, int>& b) { return a.second < b.second; // 最大堆:的在 // 若要最小堆(升序),则改为 a.second > b.second } }; int main() { map<int, int> myMap = {{1, 90}, {2, 87}, {3, 92}, {4, 90}}; // 定义优先队列,存储pair容器使用vector,并指定比较函 priority_queue<pair<int, int>, vector<pair<int, int>>, Compare> pq; // 将map中的元素加入优先队列 for (auto& kv : myMap) { pq.push(kv); } // 从优先队列中取出元素(按value到小) while (!pq.empty()) { auto top = pq.top(); cout << top.first << ": " << top.second << endl; pq.pop(); } return 0; } ``` **注意**:优先队列法会改变元素的顺序(取出时是堆顶元素),并且会消耗额外的空间。 ### 性能与场景对比 - **Vector转换法**:适用于需要一次排序并获完整排序列表的情况,简单通用。 - **构造新容器法**:适用于希望保持一个value排序容器,但后续插入新元素时不会自动维护顺序。 - **优先队列法**:适用于需要动态获最大或最小value对应的元素,但同样在插入新元素时需要手动维护。 ### 注意事项 - 当原mapvalue有重复时,Vector转换法和multimap法都能正确处理(multimap允许键重复)。而优先队列法同样可以处理重复值。 - 如果需要对value进行降序排列,只需在比较函中改变不等号方向或使用反向迭代器。 综上所述,最常用且灵活的方法是Vector转换法,因为它可以直接使用标准库的sort函,并且可以灵活定义比较规则。 §§相关问题§§ 1. 在C++中,除了vector转换法,还有哪些方法可以对mapvalue排序? 2. 如何保持一个value排序map,并且在插入新元素时自动排序? 3. 在C++中,multimapmap的主要区别是什么? </think> ### C++中根据mapvalue排序的方法 在C++中,`std::map`默认按**key排序**,无法直接按value排序。以下是三种常用方法(按推荐度排序): --- #### 方法1:Vector转换法(最通用) 1. 将map元素复制到`vector<pair<key, value>>`中 2. 使用`sort()`函+自定义比较器对vector排序 3. 时间复杂度:$O(n \log n)$,空间复杂度:$O(n)$ ```cpp #include <iostream> #include <map> #include <vector> #include <algorithm> using namespace std; // 自定义比较函(按value升序) bool cmp(const pair<int, int>& a, const pair<int, int>& b) { return a.second < b.second; } int main() { map<int, int> scores = {{1, 90}, {2, 87}, {3, 92}}; // 1. 将map元素复制到vector vector<pair<int, int>> vec(scores.begin(), scores.end()); // 2. 对vectorvalue排序 sort(vec.begin(), vec.end(), cmp); // 3. 输出结果 for (auto& p : vec) { cout << "ID: " << p.first << " Score: " << p.second << endl; } /* 输出: ID: 2 Score: 87 ID: 1 Score: 90 ID: 3 Score: 92 */ } ``` --- #### 方法2:优先队列法(适合Top K) 1. 使用`priority_queue`存储键值对 2. 自定义比较器实现堆排序 3. 时间复杂度:$O(n \log k)$(Top K时更高效) ```cpp #include <queue> // 自定义比较器(最小堆) struct Compare { bool operator()(const pair<int, int>& a, const pair<int, int>& b) { return a.second > b.second; // 改为<则为降序 } }; int main() { map<int, int> scores = {{1, 90}, {2, 87}, {3, 92}}; // 创建最小堆 priority_queue<pair<int, int>, vector<pair<int, int>>, Compare> pq; // 插入所有元素 for (auto& kv : scores) pq.push(kv); // 按value升序输出 while (!pq.empty()) { auto top = pq.top(); cout << "ID: " << top.first << " Score: " << top.second << endl; pq.pop(); } } ``` --- #### 方法3:Multimap反转法(需value唯一) 1. 创建新的`multimap<value, key>` 2. 将原map的key-value反转存入 3. 时间复杂度:$O(n \log n)$ ```cpp int main() { map<int, int> scores = {{1, 90}, {2, 87}, {3, 92}}; multimap<int, int> invertedMap; // 注意用multimap处理重复值 for (auto& kv : scores) { invertedMap.insert({kv.second, kv.first}); // 反转key-value } for (auto& kv : invertedMap) { cout << "ID: " << kv.second << " Score: " << kv.first << endl; } } ``` --- ### 关键注意事项 1. **重复值处理**:方法3需用`multimap`(允许value重复),方法1/2天然支持重复值 2. **稳定性**:方法1使用`stable_sort()`可保持相同value元素的原始顺序 3. **性能对比**: - 单次排序:方法1最佳 - 动态插入:方法2(优先队列)更高效 - 内存占用:方法3额外空间最小 > 推荐首选**Vector转换法**,因其灵活性与标准库的良好支持[^1][^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值