【问题标题】:How do I find the frequency of a given number into a range into an array?如何在数组中找到给定数字的频率?
【发布时间】:2015-07-15 10:33:33
【问题描述】:

问题是: 你得到一个大小为 N 的数组。还给出 q=查询次数;在查询中,您将得到 l= 下限,u= 上限和 num= 您必须将频率计入的数量l~u。

我在 C++ 中实现了如下代码:

#include <iostream>
#include <map>

using namespace std;

map<int,int>m;

void mapnumbers(int arr[], int l, int u)
{
    for(int i=l; i<u; i++)
    {
        int num=arr[i];
        m[num]++;
    }
}


int main()
{
    int n; //Size of array
    cin>>n;

    int arr[n];

    for(int i=0; i<n; i++)
        cin>>arr[i];

    int q; //Number of queries
    cin>>q;

    while(q--)
    {
        int l,u,num;   //l=lower range, u=upper range, num=the number of which we will count frequency
        cin>>l>>u>>num;
        mapnumbers(arr,l,u);
        cout<<m[num]<<endl;
    }

    return 0;
}

但我的代码有一个问题,在每个查询中它不会使地图 m 为空。这就是为什么如果我两次/三次查询相同的数字,它会将频率计数与之前存储的计数相加。

我该如何解决这个问题? 对于 10^5 的大范围查询,它会是一个糟糕的程序吗? 什么是解决这个问题的有效方法?

【问题讨论】:

  • 不使用全局变量?
  • 是的,对于大量查询,它的性能会很差,因为 atm 您正在为每个查询遍历数组,而在极端情况下,只遍历数组一次就足够了。
  • 如何通过只遍历数组一次来解决这个问题? @tobi303
  • 有可能,但不太实用。您可以创建一个[maxElement][size][size] 大小的结构,在其中为每个数字(第一个索引)存储下限(第二个索引)和上限(最后一个索引)之间的出现次数。这会浪费大量内存并且构建这个结构需要一些时间,但是一旦你有了它,它只是访问元素的问题。这只是交易内存与计算的极端情况。最佳算法将介于这和你的之间。很大程度上取决于细节、数组的大小、查询的数量等
  • 有没有什么方法可以通过只遍历数组一次来计算频率,并使用诸如累积和之类的任何方法计算每个查询?

标签: c++ arrays frequency cumulative-frequency


【解决方案1】:

这里的许多答案都非常复杂。我将告诉你找到范围频率的简单方法。您可以使用二进制搜索技术在每个查询 O(logn) 内获得答案。

为此,使用vector数组来存储数组中所有数字的索引值,然后使用C++ STL提供的lower_bound和upper_bound。

这里是 C++ 代码:

    #define MAX 1000010

    std::vector<int> v[MAX];

    int main(){

    cin>>n;

    for (int i = 0; i < n; ++i)
    {
        cin>>a;
        v[a].push_back(i);
    }

    int low = 0, high = 0;

    int q; //Number of queries
    cin>>q;

    while(q--)
    {
        int l,u,num;   //l=lower range, u=upper range, num=the number of which we will count frequency
        cin>>l>>u>>num;
        low = lower_bound(v[num].begin(), v[num].end(), l) - v[num].begin();
        high = upper_bound(v[num].begin(), v[num].end(), u) - v[num].begin();
        cout<<(high - low)<<endl;
    }

    return 0;
}

总体时间复杂度:O(Q*log n)

【讨论】:

    【解决方案2】:

    许多算法可用于此类问题。这看起来像一个直截了当的数据结构问题。您可以使用分段树、平方根分解。检查 Geeksforgeeks 的算法!我告诉你学习算法的原因是,这种问题有这么大的约束,如果你用你的方法,你的判决将是TLE。所以更好地使用算法。

    【讨论】:

      【解决方案3】:

      您可以使用查询的 SQRT 分解来解决该任务。复杂度将是 O(m*sqrt(n))。首先,根据以下标准对所有查询进行排序:L/sqrt(N) 应该增加,其中 L 是查询的左边界。对于相等的 L/sqrt(N),R(右边界)也应该增加。 N 是查询的数量。然后执行此操作:计算第一个查询的答案。然后,只需将该查询的边界逐一移动下一个查询的边界。例如,如果排序后的第一个查询是 [2,7],第二个是 [1, 10],则向左移动到 1 并减少 a[2] 的频率,增加 a1 的频率。将右边界从 7 移动到 10。增加 a[8]、a[9] 和 a[10] 的频率。使用您的地图增加和减少频率。这是一项非常复杂的技术,但它允许以非常复杂的方式解决您的任务。您可以在此处阅读有关 SQRT 分解查询的更多信息:LINK

      【讨论】:

      • 对这个问题来说太高级了,这里数组没有变化,简单的排序就足够了。
      【解决方案4】:

      清除地图需要拨打map::clear():

      void mapnumbers(int arr[], int l, int u)
      {
          m.clear()
      

      解决清除问题的更好方法是使m 成为while (q--) 循环甚至mapnumbers 函数的局部变量。

      但是,总的来说,您为什么需要地图是很奇怪的。反正你遍历整个数组,你就知道需要数的数,那何乐而不为呢

      int mapnumbers(int arr[], int l, int u, int num)
      {
          int result = 0;
          for(int i=l; i<u; i++)
          {
              if (arr[i] == num);
                  result ++;
          }
          return result;
      }
      

      这将更快,甚至渐近更快,因为map 操作是 O(log N),所以您的原始解决方案每个查询运行 O(N log N),而这个简单的迭代运行 O(N)。

      但是,对于一个非常大的数组和许多查询(我猜问题来自一些竞争性编程网站,不是吗?),这仍然不够。我想应该有一些允许 O(log N) 查询的数据结构和算法,虽然我现在想不出来。

      UPD:我刚刚意识到数组在你的问题中没有改变。这使它更简单,允许每个查询解决方案的简单 O(log N)。您只需对输入数组中的所有数字进行排序,同时记住它们的原始位置(并确保排序稳定,以便原始位置按升序排列);你只能这样做一次。在此之后,每个查询都可以通过两次二进制搜索来解决。

      【讨论】:

      • 谢谢。我明白。有什么方法可以通过仅遍历数组一次来计算频率,并使用诸如累积和之类的任何方法计算每个查询?
      猜你喜欢
      • 2010-09-13
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 2021-05-09
      • 1970-01-01
      • 1970-01-01
      • 2023-04-05
      • 1970-01-01
      相关资源
      最近更新 更多