C++ equivalent of Python difference_update?

C++ equivalent of Python difference_update?

本文关键字:update difference Python equivalent of C++      更新时间:2023-10-16

s1 和 s2 是集合(Python set 或 C++ std::set)
要将 s2 的元素添加到 s1(集合联合),您可以执行

Python: s1.update(s2)
C++: s1.insert(s2.begin(), s2.end());

要从 s2 中删除 s1 的元素(设置差分),您可以执行

Python: s1.difference_update(s2)

这C++等价物是什么?代码

s1.erase(s2.begin(), s2.end());

不起作用,因为 s1.erase() 需要来自 s1 的迭代器。代码

std::set<T> s3;
std::set_difference(s1.begin(), s1.end(), s2.begin(), s2.end(), std::inserter(s3, s3.end());
s1.swap(s3);

工作,但似乎过于复杂,至少与 Python 相比。

有没有更简单的方法?

使用 std::set_difference 是C++中执行此操作的惯用方法。 您偶然发现了C++/STL与许多其他语言之间的主要差异之一(双关语)。 STL 不直接将操作与数据结构捆绑在一起。 这就是std::set不实现差异例程的原因。

基本上,诸如std::set_difference之类的算法将操作的结果写入另一个对象。 有趣的是,该算法并不要求一个或两个操作数实际上是std::set 。 该算法的定义是:

效果:将范围[first1, last1)中不存在的范围元素复制到从 result 开始的范围[first2, last2)。 对构造区域中的元素进行排序。

要求:生成的范围不得与任一原始范围重叠。 输入范围需要按同一operator<排序。

返回:构造范围的结束。

复杂性:最多2 * ((last1 - first1) + (last2 - first2)) - 1比较

有趣的区别在于C++版本适用于任何两个排序范围。 在大多数语言中,您被迫强制或将调用对象(左操作数)转换为集合,然后才能访问集合差分算法。

这与你的问题并不真正相关,但这就是各种集合算法被建模为独立算法而不是成员方法的原因。

你应该遍历第二个集合:

for( set< T >::iterator iter = s2.begin(); iter != s2.end(); ++iter )
{
    s1.erase( *iter );
}

这可能比使用 std::set_difference 便宜 - set_difference将唯一对象复制到新容器中,但这需要线性时间,而.erase不会复制任何内容,而是O(n * log( n ) )

换句话说,取决于容器,您可以选择方式,这对您的情况来说会更快。

谢谢David Rodríguez - dribeas的评论!(:


编辑:嘟!我一开始就想过BOOST_FOREACH,但我错了,它不能使用。- 你不需要迭代器,只需要值。正如用户763305自己所说。

在 c++ 中,集合中没有difference方法。set_difference看起来更笨拙,因为它比在两组上应用差异更通用。当然,你可以在集合上实现你自己的就地差分版本:

template <typename T, typename Compare, typename Allocator>
void my_set_difference( std::set<T,Compare,Allocator>& lhs, std::set<T,Compare,Allocator> const & rhs )
{
    typedef std::set<T,Comapre,Allocator> set_t;
    typedef typename set_t::iterator iterator;
    typedef typename set_t::const_iterator const_iterator;
    const_iterator rit = rhs.begin(), rend = rhs.end();
    iterator it = lhs.begin(), end = lhs.end();
    while ( it != end && rit != rend )
    {
        if ( lhs.key_comp( *it, *rit ) ) {
            ++it;
        } else if ( lhs.key_comp( *rit, *it ) ) {
            ++rit;
        } else {
            ++rit;
            lhs.erase( it++ );
        }
    }
}

此算法的性能在参数大小上将是线性的,并且不需要额外的副本,因为它会就地修改第一个参数。

你也可以通过编写自己的函子来remove_if测试集合中的存在性,例如

std::remove_if(s1.begin(), s1.end(), ExistIn(s2));

我想set_difference更有效,因为它可能只扫描两组一次

Python set 是无序的,比 std::set 更等同于 C++ std::

unordered_set,而不是 std::set,它是有序的。

David Rodríguez 的算法依赖于 std::set 是有序的,因此 lhs 和 rhs 集合可以按照算法中所示的方式遍历。

对于适用于有序和无序集合的更通用的解决方案,如果您正在强制执行/保留 Python 集合的"无序"性质,Kiril Kirov 的算法应该是安全的算法。