簡體   English   中英

獲取大於一個數字的元素數量

[英]Get number of elements greater than a number

我正在嘗試解決以下問題:正在將數字插入到容器中。 每次插入一個數字時,我都需要知道容器中有多少元素大於或等於當前插入的數字。 我相信這兩種操作都可以在對數復雜度中完成。

我的問題: C++ 庫中是否有可以解決問題的標准容器? 我知道std::multiset可以在對數時間內插入元素,但是如何查詢呢? 或者我應該實現一個數據結構(例如二叉搜索樹)來解決它?

很好的問題。 我認為 STL 中沒有任何東西可以滿足您的需求(前提是您必須有對數時間)。 正如 aschepler 在評論中所說,我認為最好的解決方案是實現 RB 樹。 您可以查看 STL 源代碼,特別是在stl_tree.h ,看看您是否可以使用它的一部分。

更好的是,看看:( C++ 中的排名樹

其中包含實現的鏈接:

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

您應該使用多重集來計算對數復雜度,是的。 但是計算距離是問題所在,因為 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)。 如果預先知道插入大小,則在已排序的容器中不需要對數插入時間。 您可以將所有的物品然后進行排序,這盡可能多的O(n.log(N))為N * O(日志(n))的一組插入。 唯一的選擇是使用專用容器,如加權 RB 樹。 根據您的問題,這可能解決方案,或東西真的矯枉過正。

  • 使用multisetdistance ,你是 O(n.log(n)) 插入(是的,n 插入 * log(n) 插入時間為每個),O(nn) 在距離計算上,但計算距離非常快。
  • 如果你事先知道插入的數據大小(n):使用向量,填充它,對其進行排序,返回你的距離,你是O(n.log(n)),並且很容易編碼。
  • 如果您事先不知道 n,您的 n 可能很大,每個項目都占用大量內存,因此您不能進行 O(n.log(n)) 重新分配:那么您有時間重新編碼或重新使用一些非標准代碼,你真的必須滿足這些復雜性期望,使用專用容器。 還要考慮使用數據庫,您可能會在內存中維護它。

這是在 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 的 GitHub

聽起來像是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 函數的語法問題

如果整個數字范圍足夠小(大約幾百萬),則可以使用Fenwick 樹相對容易地解決此問題。

盡管Fenwick 樹不是 STL 的一部分,但它們都非常容易實現且省時。 更新和查詢的時間復雜度都是O(log N) ,並且常數因子很低。

你在另一個問題的評論中提到,你需要這個來參加比賽。 Fenwick 樹是競爭性編程中非常流行的工具,通常很有用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM