如何从向量或数组中选择最常见的数字?(TOP5 toplist)C++

Posted

技术标签:

【中文标题】如何从向量或数组中选择最常见的数字?(TOP5 toplist)C++【英文标题】:How to select the most common numbers from a vector or on array?(TOP5 toplist) C++ 【发布时间】:2020-01-30 11:45:11 【问题描述】:

我有一个带整数的向量。我想选择最常见的数字。我想列出前 5 名。例如:

std::vector<int> numbers = 32, 32, 32, 12, 12, 11, 11, 11, 9;

最常见的数字是:TOP 1:32,TOP 2:11,TOP 3:12,TOP 4:9;

选择它们后,我想将其存储到另一个向量中:最常见的数字。

【问题讨论】:

【参考方案1】:

这是另一种算法,任何k 的成本将是 O(n),再加上一个不错的缓存位置。

1.首先将所有元素存储在一个unordered_mapO(N)中

std::unordered_map<int, int> m;
for(const auto ele: numbers) 
  m[ele]++;   

2.将所有元素转储到成对向量中 O(N)

std::vector<std::pair<int, int>> temp;  
for(const auto& ele: m) 
 temp.emplace_back(ele.first ,  ele.second);   

3.现在使用第n个元素找到第k个等级O(N)

std::nth_element( temp.begin(), temp.begin()+k ,
temp.end(), [](const auto& p1, const auto& p2) 
        // Strict weak ordering
        if (p1.second > p2.second) 
            return true;
          if (p1.second < p2.second)   
            return false;
        
        return p1.first > p2.first; // We have to print large element first
     );

4.显示输出

std::for_each( temp.begin(), temp.begin() +k - 1, [](const auto & p) 
    std::cout << p.first << " ";
);

Demo Here

【讨论】:

4使用std::for_each_n @Verthais 谢谢,是的,std::for_each_n 也可以使用。不确定哪个 libstdc++ 有它 如果唯一编号少于k,这将打印出垃圾,不是吗?【参考方案2】:

您可以创建一个unordered_map&lt;int,int&gt; mp,在其中存储每个数字的计数,例如mp[32] = 3

接下来你需要找到前五个元素

    时间复杂度:O(mlogm):您可以按降序对地图进行排序(要对其进行排序,您必须使用额外的向量),然后取前 5 个元素。

    时间复杂度:O(m):或者您可以在整个地图上迭代 5 次以获得顶部文件元素。每次迭代时,找到我们的 topFive 向量中不存在的频率最高的数字。

m : 地图中的条目数。

【讨论】:

这太过分了,应该首选std::unordered_map应该首选 @POW 知道了,在这种情况下unordered_map 是首选。【参考方案3】:

我已经制作了这个示例并将 cmets 串联起来。它至少需要 C++11。

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

int main(void) 
  std::map<int, int> ans;
  std::vector<int> numbers = 32, 32, 32, 12, 12, 11, 11, 11, 9;
  std::vector<std::pair<int, int>> sorted;
  std::vector<int> common;
  // Step 1 Accumulate into a map, counting occurrences
  for (auto number : numbers) 
    ans[number]++;
  
  // Step 2 Make a linear list, putting the count first then the value
  for (auto& ent : ans) 
    sorted.emplace_back(ent.second, ent.first);
  
  // Step 3 sort it, by count ascending
  std::sort(std::begin(sorted), std::end(sorted));

  // Step 4 Get commonest 5 (or fewer)
  for (int i = 1; i<= 5; ++i) 
    int index = sorted.size() - i;
    if (index >= 0) 
      common.push_back(sorted[index].second);
    
  

  // Step 5 print out
  for (auto i : common) 
    std::cout << i << std::endl;
  
  return 0;

【讨论】:

我们不需要完全排序。部分排序就可以了。【参考方案4】:

你可以这样做:创建一个集合,这样你就可以摆脱重复了,然后在向量中找到集合中每个项目的频率,用这个结果创建一个对(类似于 int,int)将这个对推入一个向量,最后使用您自己的谓词对其进行排序: 现在对于顶部 x,您可以执行一个 for 循环,或者如果您确定这样做的后果是什么,则只需调整向量的大小。

std::vector<int> numbers32, 32, 32, 12, 12, 11, 11, 11, 9;
std::set<int> mySet(numbers.begin(), numbers.end());
std::vector<std::pair<int, int>> result;

for(const auto& x : mySet)

    result.push_back(std::make_pair(x , std::count(numbers.begin(), numbers.end(), x)));

std::sort(result.begin(), result.end(), [](const std::pair<int, int>& a, const std::pair<int, int>& b)return (b.second < a.second););
//result.resize(3);
std::cout << "Top: " << std::endl;
for(const auto& x : result)

   std::cout << x.first << ':' << x.second << std::endl;

结果将是:

顶部:11:3 32:3 12:2 9:1

【讨论】:

部分排序可以。加上std::count 是线性的。你的第一个循环是 N*N 最坏的情况。【参考方案5】:

有很多方法可以实现这一点。其中之一可能是。

std::vector numbers = 32, 32, 32, 12, 12, 11, 11, 11, 9;
int maxNumber = *std::max_element(numbers.begin(), numbers.end())
std::vector<int> occurrences(maxNumber + 1, 0);
for(auto& value : numbers)

   occurrences[value]++;

然后您只需要在跟踪索引的同时对数组进行排序。这是另一个问题C++ sorting and keeping track of indexes 的话题。

【讨论】:

以上是关于如何从向量或数组中选择最常见的数字?(TOP5 toplist)C++的主要内容,如果未能解决你的问题,请参考以下文章

从向量中提取最小值、最大值和中值的最有效方法是啥

Verilog定义向量的数组后如何选择某个向量?

当位置超出范围时如何从数组中选择一个值?

2019年最值得学习的编程语言TOP5,C#上榜了

如何从 Patient_id 中找到最常见的数字

如何将文件中的整数读入动态数组