一种更有效的计数间隔内值数量的方法
More efficient way of counting the number of values within an interval?
我想确定在每个给定的间隔(多个)中有多少个输入数组(最多50000个)。
目前,我正试图用这个算法来做这件事,但它太慢了:
示例数组:{-3、10、5、4、-9999999、999999、6000}
示例间隔:[0,11](含)
- 排序数组-
O(n * log(n))
。(-9999999,-3,4,5,10,6000,999999) - 查找min_index:
array[min_index] >= 0
-O(n)
。(对于我的示例,min_index==2) - 查找最大索引:
array[max_index] <= 11
-O(n)
。(例如,max_index==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间隔)算法:
为了便于实现,我们使用半开放间隔。根据需要转换您的。
- 将音程转换为半开放音程(总是更喜欢半开放音距)
- 将所有限制保存在一个数组中
- 对于输入中的所有元素
- 对于极限数组中的所有元素
- 如果输入小于限制,则增加计数
- 对于极限数组中的所有元素
- 通过你的时间间隔,减去相应极限的计数得到答案
为了稍微提高性能,请在步骤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是每个区间重叠的区间的平均数量。
- 有符号的int和int-有没有一种方法可以在C++中区分它们
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 有没有一种方法可以在编译时获得作用域类名
- 对于C++中使用智能指针的指针算术限制,有没有一种变通方法
- 一种在C++中读取TXT配置文件的简单方法
- 有没有一种方法可以测量c++程序的运行时内存使用情况
- 有没有一种方法可以使用placement new将堆叠对象分配给分配的内存
- 在调用接收数组的方法时,模板化数组大小是不是一种糟糕的做法
- 有没有一种方法可以通过"typedef"为重新定义的基本类型定义特征和强制转换运算符
- 有没有一种"cleaner"的方法可以在指向基的指针向量中找到派生类的第一个实例?
- 有没有一种代码密度较低的方法来使用非默认构造函数初始化数组?
- 在 c++ 中,有一种方法可以创建一个包含地图作为值的树状地图?
- 有没有一种优雅而快速的方法来测试整数中的 1 位是否位于连续区域
- C++ STD 函数运算符:有没有一种方法可以通过函数将一个向量映射到另一个向量上?
- 找到一种有效的方法,在 2 个巨大的缓冲区上执行 MAX,每字节字节
- 寻找一种更好的方法来表示无符号字符数组
- 有没有一种通用的方法来实现不变量
- 有没有一种方法可以捕获进程中的堆栈溢出?C++Linux
- 使用类在C++中存储和列出变量/方法是否是一种好的做法
- 一种从内存中删除 UTF 字节的方法?