查找最多次出现的数组中的数字

时间:2010-10-12 13:47:41

标签: c++ algorithm

给定一个整数数组,我需要找到最多次出现的数字。 我编写了如下算法。

  
      
  1. 使用地图存储发生的次数和次数。

         

    map<int, int>

         

    键:代表数字
      value:表示密钥发生的次数。

  2.   
  3. 扫描输入数组并使用编号和出现次数更新地图。
  4.   
  5. 从头到尾迭代地图。找到钥匙   哪个最大值存在。这个   key成为发生的数字   最多次。
  6.   

我实现了如下算法。

#include <iostream> 
#include <map>
using namespace std; 
int main()
{
    int a[10] = {1,2,3,2,1,3,2,4,1,1}; //Input array: hardcoded for testing
    map<int, int> m;

    for(int i=0;i<10;i++)
    {
        m[a[i]]++;  //Increment the value of key for counting occurances
    }

    int mostNumTimes = 0; 
    int number = -999; //-999 represents invalid number
    map<int,int>::iterator it = m.begin();
    for( ;it != m.end(); it++)  //Find the number which occurred 
    {                           //most number of times
        if(it->second > mostNumTimes)
        {
            mostNumTimes = it->second;
            number = it->first;
        }
    }
    if(number != -999)   //Print number and number of times it occurred
    {
        cout<<"Number: "<<number<<endl;
        cout<<"Number of times occured: "<<mostNumTimes<<endl;
    }
    else
    {
        cout<<"Input array is empty"<<endl;
    }
    return 0;
}
  

输出:

     

数量:1

     

发生的次数:4

问题:有没有最佳方法? 最后,我正在迭代整个地图,因为我找不到任何成员函数来查找其值在地图中最高的键。我可以避免迭代所有键吗?

注意:我没有考虑多个号码是否发生相同的次数。我发现第一个出现次数最多的数字。

5 个答案:

答案 0 :(得分:8)

您可以在迭代值时保持当前最大值(count和int值)。在地图中的每个增量上,您可以更新值,这样就不必在最后进行迭代。

map<int, int> m;
int currentMax = -999;
int maxCount = 0;
for(int i=0;i<10;i++)
{
    int updated = m[a[i]]++;  //Increment the value of key for counting occurances        
    updated++; // due to post increment 
    if (maxCount < updated) {
         maxCount = updated;
         currentMax = i;
    }
}

因为这是一项有趣的练习,我们似乎都假设地图迭代很便宜。虽然迭代地图也是O(N),但它比迭代矢量或数组要昂贵得多。那么什么更便宜,迭代一个可能缩小的大小的地图,或做一个非常基本的检查,将以某个百分比触发两个任务?假设您更改为使用无序映射,那么您的解决方案和此解决方案都是O(N)。现在你不是,所以每个插入都是log(n),实际上我认为切换到无序地图将是你最大的收获。

答案 1 :(得分:4)

你的算法非常好。它实际上是O(N Log N),因为您正在进行N std :: map(基于树的地图)插入(每个Log N)。这决定了算法的时间复杂度,因为第二阶段是线性的。

改进将是使用哈希映射,为您提供整体的线性算法。

答案 2 :(得分:2)

对数组进行排序,以便...

{1,1,1,1,2,2,2,3,3,4,4}

然后有一个currentValue变量,当值不匹配时设置它,当它设置时,递增计数...即(伪代码)

currentValue = 0;
currentCount = 0;
maxValue = 0;
maxCount = 0;

for(int value in array) {
  if(currentValue == value) {
    currentCount++;
  } else {
    // is this greater than max count
    if(currentCount > maxCount) {
      maxCount = currentCount;
      maxValue = currentValue;
    }

    // reset values
    currentValue = value;
    currentCount = 0;
  }
}

现在您拥有maxValue中出现次数最多的值以及maxCount中出现的次数。

答案 3 :(得分:0)

首先,你应该摆脱无效的数字-999。在继续之前,请先询问map.empty()。

然后,我认为增加地图中之前不存在的元素是无效的。我假设使用unitialized(random)值创建了一个新成员,因为int没有默认构造函数。

您可以做其他事情:

map<int, int>::iterator it = m.find(i);
if (it != m.end())
    m.second++;
    if (m.second > mostTimes) {
      // reset mostTimes and maxNumber = m.first here
    }
} else {
    m[i] = 1;
}

此操作为O(n),因此具有相同的时间复杂度类,再次迭代地图以找到最大元素(在最坏的情况下,输入中的所有数字都不同,并且地图具有相同的数量成员n比输入数组)。但不同之处在于,大多数Time和maxNumbers可能会被覆盖很多次,并且可能会发生它们不适合CPU寄存器并且会发生大量RAM访问。因此,事后进行迭代可能会更快。

答案 4 :(得分:0)

需要线性迭代(正如人们已经提到的那样),但是给定顺序并不重要,你可以折叠数组吗?这样做可以为相同的元素保存两个地图更新,即

int i = 0;
int j = sizeof(a)/sizeof(int);
for(;i < j;i++, j--)
{
  if (a[i] == a[j])
  {
    update<map_t, 2>(m, a[i]);
  }
  else
  {
    update<map_t, 1>(m, a[i]);
    update<map_t, 1>(m, a[j]);
  }
}
// if array size is odd...
if (i == j)
    update<map_t, 1>(m, a[i]);

这里update是一个简单的函数,因为我懒得输入相同的代码......

template <typename M, int DEF>
void update(M& m, int v)
{
  typename M::iterator it = m.find(v);
  if (it != m.end())
      it->second += DEF;
  else
  {
    m.insert(pair<int, int>(v, DEF));
  }
}

其他一切都保持不变,即你的代码很好,只有轻微的改进......