一种更有效的计数间隔内值数量的方法

More efficient way of counting the number of values within an interval?

本文关键字:方法 一种 有效      更新时间:2023-10-16

我想确定在每个给定的间隔(多个)中有多少个输入数组(最多50000个)。

目前,我正试图用这个算法来做这件事,但它太慢了:

示例数组:{-3、10、5、4、-9999999、999999、6000}
示例间隔:[0,11](含)

  1. 排序数组-O(n * log(n))。(-9999999,-3,4,5,10,6000,999999)
  2. 查找min_index:array[min_index] >= 0-O(n)。(对于我的示例,min_index==2)
  3. 查找最大索引:array[max_index] <= 11-O(n)。(例如,max_index==4)
  4. 如果两个索引都存在,那么Result == right_index - left_index + 1(对于我的示例,Result=(4-2+1)=3)

您有一个好主意,但它需要修改。您应该使用二进制搜索在O(lg n)时间中找到间隔的开始和结束。如果n是数组的长度,q是问题数[a, b],则您有O(n+q*n)时间,对于二进制搜索,它是O((n + q) lg n)(排序数组中的n lg n)。这个解决方案的优点是简单,因为C++有std::lower_bound和std::upper_bound。你可以使用std::distance。这只是几行代码。

如果q等于n,则该算法具有O(n lg n)复杂度。可以更好吗?一点也不。为什么?因为这个问题相当于排序。众所周知,不可能获得更好的计算复杂度。(通过比较进行排序。)

有一个简单的O(n输入*m间隔)算法:

为了便于实现,我们使用半开放间隔。根据需要转换您的。

  1. 将音程转换为半开放音程(总是更喜欢半开放音距)
  2. 将所有限制保存在一个数组中
  3. 对于输入中的所有元素
    • 对于极限数组中的所有元素
      • 如果输入小于限制,则增加计数
  4. 通过你的时间间隔,减去相应极限的计数得到答案

为了稍微提高性能,请在步骤2中对极限数组进行排序。

创建一个std::映射,将数字映射到排序数组中的索引。

根据您的示例map[-999999]=0,map[-3]=1。。。map[999999]=7。

要查找区间,请查找高于或等于最小值的最低数字(使用map.lower_bound()),然后查找高于最大值的第一个数字(使用map.opper_bound)。

现在,您可以从上索引中减去下索引,以在O(logn)中找到该范围内的元素数量。

typedef std::pair<int,int> interval;
typedef std::map<interval,size_t> answers;
typedef std::vector<interval> questions;
// O((m+n)lg m)
answers solve( std::vector<int>& data, questions const& qs ){
  // m = qs.size()
  // n = data.size()
  answers retval;
  std::vector<std::pair<int, size_t>> edges;
  edges.reserve( q.size()+1 );
  // O(m) -- all start and ends of intervals is in edges
  for ( auto q:qs ) {
    edges.emplace_back( q.first, 0 );
    edges.emplace_back( q.second, 0 );
  }
  // O(mlgm) -- sort
  std::sort(begin(edges),end(edges));
  edges.emplace_back( std::numeric_limits<int>::max(), 0 );
  // O(m) -- remove duplicates
  edges.erase(std::unique(begin(edges),end(edges)),end(edges));
  // O(n lg m) -- count the number of elements < a given edge:
  for(int x:data ){
    auto it = std::lower_bound( begin(edges), end(edges), std::make_pair(x,0) );
    it->second++;
  }
  // O(m)
  size_t accum = 0;
  for(auto& e:edges) {
    accum += edges.second;
    edges.second = accum;
  }
  // now edge (x,y) states that there are y elements < x.
  // O(n lg m) -- find the edge corresponding
  for(auto q:questions){
    auto low = std::lower_bound(begin(edges), end(edges),
      std::make_pair(q.first, size_t(0))
    );
    auto high = std::upper_bound(begin(edges), end(edges),
      std::make_pair(q.second, size_t(0))
    }
    size_t total = high->second - low->second;
    answers.emplace(q,total);
  }
  return answers;
}

O((n+m)lg m),其中n是整数计数,m是区间的数量,x是每个区间重叠的区间的平均数量。

相关文章: