我正在尝试解决以下问题:正在将数字插入容器中。每次插入一个数字时,我都需要知道容器中有多少元素大于或等于当前插入的数字。我相信这两项操作都可以以对数复杂度完成。
我的问题:
C ++库中是否有可以解决问题的标准容器?
我知道std::multiset
可以在对数时间插入元素,但是如何查询它?或者我应该实现一个数据结构(例如,二叉搜索树)来解决它?
答案 0 :(得分:4)
好问题。我不认为STL中有任何东西可以满足您的需求(前提是你必须有对数时间)。我认为,正如aschepler在评论中所说的那样,最佳解决方案是实现RB树。您可以查看STL源代码,尤其是在stl_tree.h
上查看是否可以使用它的位。
更好的是,看看:(Rank Tree in C++)
其中包含实施链接:
答案 1 :(得分:1)
你应该使用多重集来实现对数复杂性,是的。但计算距离是个问题,因为set / map迭代器是双向的,而不是RandomAccess,std :: distance对它们有O(n)的复杂性:
multiset<int> my_set;
...
auto it = my_map.lower_bound(3);
size_t count_inserted = distance(it, my_set.end()) // this is definitely O(n)
my_map.insert(make_pair(3);
您的复杂性问题很复杂。这是一个完整的分析:
如果您希望每次插入都有O(log(n))复杂度,则需要排序结构作为集合。如果希望结构在添加新项目时不重新分配或移动项目,则插入点距离计算将为O(n)。如果事先知道插入大小,则不需要在已排序容器中使用对数插入时间。您可以插入所有项目然后排序, 与集合中的n * O(log(n))插入一样多O(n.log(n))。 唯一的选择是使用像加权RB树这样的专用容器。根据您的问题,这可能是 解决方案,或者真的有点过分。
multiset
和distance
,你在插入时是O(n.log(n))(是的,n插入* log(n)插入时间为每一个),O( nn)关于距离计算,但计算距离非常快。答案 2 :(得分:0)
听起来像是count_if
的情况 - 尽管我承认这并不能在对数复杂度下解决它,但这需要一个排序类型。
vector<int> v = { 1, 2, 3, 4, 5 };
int some_value = 3;
int count = count_if(v.begin(), v.end(), [some_value](int n) { return n > some_value; } );
编辑完成以修复lambda函数的语法问题
答案 3 :(得分:0)
如果整个数字范围足够小(大约几百万),使用Fenwick tree可以相对容易地解决这个问题。
虽然Fenwick trees不是STL的一部分,但它们都非常容易实现且时间效率很高。更新和查询的时间复杂度为O(log N)
,常数因子较低。
您在another question的评论中提到,您需要参加比赛。 Fenwick trees是竞争性编程中非常受欢迎的工具,通常很有用。
答案 4 :(得分:0)
存在一种称为“有序集”的东西,它使您可以在O(logN)时间内插入/删除元素(以及std :: set必须提供的几乎所有其他功能)。它还提供了2个其他功能:查找第K个元素并**查找第X个元素的排名。问题是这不允许重复:(
不过不用担心!我们将使用单独的索引/优先级映射重复项,并定义一个新结构(称为有序多重集)!我在下面附上了我的实现以供参考。
最后,每次您要查找大于x的元素数量时,请调用函数upper_bound(小于或等于x的元素数量),然后从有序多重集合的大小中减去该数字!
注意:PBDS使用大量内存,因此这是一个限制,我建议使用二进制搜索树或Fenwick树。
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
struct ordered_multiset { // multiset supporting duplicating values in set
int len = 0;
const int ADD = 1000010;
const int MAXVAL = 1000000010;
unordered_map<int, int> mp; // hash = 96814
tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> T;
ordered_multiset() { len = 0; T.clear(), mp.clear(); }
inline void insert(int x){
len++, x += MAXVAL;
int c = mp[x]++;
T.insert((x * ADD) + c); }
inline void erase(int x){
x += MAXVAL;
int c = mp[x];
if(c) {
c--, mp[x]--, len--;
T.erase((x*ADD) + c); } }
inline int kth(int k){ // 1-based index, returns the
if(k<1 || k>len) return -1; // K'th element in the treap,
auto it = T.find_by_order(--k); // -1 if none exists
return ((*it)/ADD) - MAXVAL; }
inline int lower_bound(int x){ // Count of value <x in treap
x += MAXVAL;
int c = mp[x];
return (T.order_of_key((x*ADD)+c)); }
inline int upper_bound(int x){ // Count of value <=x in treap
x += MAXVAL;
int c = mp[x];
return (T.order_of_key((x*ADD)+c)); }
inline int size() { return len; } // Number of elements in treap
};
ordered_multiset s;
for(int i=0; i<n; i++) {
int x; cin>>x;
s.insert(x);
int ctr = s.size() - s.upper_bound(x);
cout<<ctr<<" ";
}
输入(n = 6):10 1 3 3 2
输出:0 1 1 1 3
参考文献:mochow13's GitHub