【问题标题】:Unable to erase from libstdc++ Policy based data structure无法从基于 libstdc++ 策略的数据结构中删除
【发布时间】:2020-01-14 10:30:30
【问题描述】:

C++中有一个关联的容器,它实际上是一个集合(multiset),它可以给出一个元素在其中的顺序。

这是我使用容器的方式:

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>

using namespace std;
using namespace __gnu_pbds;

template <typename T>
using ordered_multiset = tree<T, null_type, less_equal<T>, rb_tree_tag,
                              tree_order_statistics_node_update>;

问题是,我无法从中删除元素:

ordered_multiset<int> s;
s.insert(0);
s.erase(0);

cout << s.order_of_key(1); // returns number of elements less than x

// Outputs: 1

奇怪的是,如果你将less_equal 替换为less,那么你将能够毫无问题地完成这项工作,实际上如果你将容器用作多重集,那么你将无法擦除其中的元素,但是当你将它作为一个集合使用时没有问题。

  • 导致问题的原因是什么?
  • 我该如何解决这个问题?

注意:请不要建议使用其他容器来解决问题。这不是解决方案。

【问题讨论】:

  • 为什么不使用std::multiset
  • 与其依赖using namespace std;using namespace __gnu_pbds;,不如明确说明您正在谈论的命名空间。 ordered_multiset 不是 c++ 的一部分,而是一些不常见的扩展
  • 我找不到O(1) 的声明来自order_of_key,但我对此非常怀疑,因为查找是O(log(n))
  • 找不到文档是不使用这个库而是使用标准库的另一个原因
  • linear 还是比 unimplemented 好,比 unimplementable 好

标签: c++ stl containers libstdc++


【解决方案1】:

使用std::less_equal,无法知道两个元素是否相等。 std::set 使用表达式!comp(a, b) &amp;&amp; !comp(b, a) 来确定@​​987654326@ 和b 是否等价。如果您使用 strict weak ordering(如 std::less),则此方法有效,但在使用 std::less_equal 时失败。 55 是等价的,但 !(5 &lt;= 5) &amp;&amp; !(5 &lt;= 5)false。所以擦除失败。

简而言之,您不能只使用std::less_equal 将一个集合变成一个多重集合。


@Caleth 描述了一种使用std::multiset 并在线性时间内进行搜索的方法。您可以通过保持每个元素的顺序来确定对数时间的顺序。

template <typename Value>
struct ordered_value {
  size_t order;
  Value value;
};

template <typename Value>
using ordered_multiset = std::multiset<ordered_value<Value>>;

这样做的问题是每次插入或擦除时都必须更新顺序(这是线性的)。您确定您使用的容器在恒定时间内执行操作吗?对我来说,这似乎是不可能的。

排序统计在红黑树中的实现方式实际上非常相似。每当您插入或擦除时,每个节点中都会更新一些额外信息。关键是,这已经非常接近您可以做到的最佳效果了,而无需费心制作自己的红黑树。

【讨论】:

    【解决方案2】:

    导致问题的原因是什么?

    您尚未向tree 提供Compare。你的程序格式不正确。

    我该如何解决这个问题?

    使用std::multiset&lt;T&gt;

    template<typename Set, typename Key>
    size_t order_of_key(const Set & set, const Key & key)
    {
        return std::distance(set.begin(), set.lower_bound(key));
    }
    

    【讨论】:

    • 如果你的意思是std::multiset,那么我不能使用多重集,因为我需要多重集容器中没有的函数order_of_key
    • std::distance 在 O(n) 中,n = 两个元素的距离。
    • 实际上是否必须是upper_bound 才能使第一个元素大于搜索的元素?这个问题在这方面有点不清楚。
    【解决方案3】:

    我知道这篇文章已有一年多的历史了,但我一直在努力解决同样的问题,并认为我找到了一个不太优雅但有用的解决方案。

    您可以像这样创建自己的erase 函数:

    void myerase(ordered_set &t, int v){
        int rank = t.order_of_key(v);//Number of elements that are less or equal to v in t
        ordered_set::iterator it = t.find_by_order(rank); //Iterator that points to the (rank)th element in t
        t.erase(it);
    }
    

    由于order_of_keyfind_by_order和标准erase都是O(log(N))myerase也应该是O(log(N))。如果我错了,请有人纠正我。

    这是我用来测试的代码sn-p:

    #include<iostream>
    #include <ext/pb_ds/assoc_container.hpp> 
    #include <ext/pb_ds/tree_policy.hpp> 
    
    using namespace __gnu_pbds; 
    using namespace std;
    
    typedef tree<int,null_type,less_equal<int>,rb_tree_tag,tree_order_statistics_node_update> ordered_set;
    
    void myerase(ordered_set &t, int v){
        int rank = t.order_of_key(v);//Number of elements that are less or equal to v in t
        ordered_set::iterator it = t.find_by_order(rank); //Iterator that points to the (rank)th element in t
        t.erase(it);
    }
    
    void printOrderedSet(ordered_set s){
        //Function to show the contents of the set
        for(auto it=s.begin(); it!=s.end(); it++){
            cout<<*it<<" ";
        }cout<<endl;
    }
    
    int main(){
        //Create an ordered_set t with the numbers 0,0,1,1,2,2,3,3,4,4
        ordered_set t;
        for(int i=0; i<10;i++){
            t.insert(i/2);
        }
        printOrderedSet(t); //output: 0 0 1 1 2 2 3 3 4 4
        
        myerase(t, 3);
        printOrderedSet(t); //output: 0 0 1 1 2 2 3 4 4
    
        myerase(t, 3);
        printOrderedSet(t); //output: 0 0 1 1 2 2 4 4
    
        myerase(t, 0);
        printOrderedSet(t); //output: 0 1 1 2 2 4 4
    
        myerase(t, 1);
        printOrderedSet(t); //output: 0 1 2 2 4 4
    }
    

    干杯!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-26
      • 2021-06-30
      • 1970-01-01
      • 1970-01-01
      • 2014-10-19
      相关资源
      最近更新 更多