获取大于数字的元素数量

时间:2013-07-02 14:54:31

标签: c++ algorithm containers

我正在尝试解决以下问题:正在将数字插入容器中。每次插入一个数字时,我都需要知道容器中有多少元素大于或等于当前插入的数字。我相信这两项操作都可以以对数复杂度完成。

我的问题: C ++库中是否有可以解决问题的标准容器? 我知道std::multiset可以在对数时间插入元素,但是如何查询它?或者我应该实现一个数据结构(例如,二叉搜索树)来解决它?

5 个答案:

答案 0 :(得分:4)

好问题。我不认为STL中有任何东西可以满足您的需求(前提是你必须有对数时间)。我认为,正如aschepler在评论中所说的那样,最佳解决方案是实现RB树。您可以查看STL源代码,尤其是在stl_tree.h上查看是否可以使用它的位。

更好的是,看看:(Rank Tree in C++

其中包含实施链接:

http://code.google.com/p/options/downloads/list

答案 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树这样的专用容器。根据您的问题,这可能是 解决方案,或者真的有点过分。

  • 使用multisetdistance,你在插入时是O(n.log(n))(是的,n插入* log(n)插入时间为每一个),O( nn)关于距离计算,但计算距离非常快。
  • 如果您事先知道插入的数据大小(n):使用向量,填充,排序,返回距离,您是O(n.log(n)),并且很容易编码。< / LI>
  • 如果您事先不知道n,那么您的n可能很大,每个项目都是内存繁重的,所以您不能重新分配O(n.log(n)):那么您有时间重新编码或重新编码 - 使用一些非标准代码,你真的必须满足这些复杂性期望,使用专用容器。另外考虑使用数据库,在内存中维护它时可能会遇到问题。

答案 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)

这是在C ++中使用基于策略的数据结构的快速方法:

存在一种称为“有序集”的东西,它使您可以在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

时间复杂度:每个查询/插入的O(log n)

参考文献:mochow13's GitHub