【问题标题】:C++ equivalent of Python difference_update?C++ 相当于 Python 的差异更新?
【发布时间】:2011-08-30 13:14:47
【问题描述】:

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

Python: s1.update(s2)

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

要从 s1 中删除 s2 的元素(设置差异),您可以这样做

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 相比。

有没有更简单的方法?

【问题讨论】:

  • +1 好问题!我很确定这可以使用std::for_each 来完成,但仍然不确定具体如何。
  • 嗯,看来for_each 在这里无济于事.. 甚至 BOOST_FOREACH :\\
  • 你可以做 BOOST_FOREACH(const T& t, s2) s1.erase(t);
  • 噢!当然你可以使用它,因为你只需要值,而不是迭代器..

标签: c++ python stdset


【解决方案1】:

你应该遍历第二组:

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 的评论! (:


编辑:Doh!我一开始就想到了 BOOST_FOREACH,但我错了它不能使用.. - 你不需要迭代器,只需要值.. 正如 user763305 自己说的那样。

【讨论】:

  • 这比set_difference 更便宜还是更贵取决于输入。对于两个大的相等集合set_difference 在集合的大小上是线性的,但这种方法是O(N log N)(对于一个集合中的每个元素,在另一个集合中执行查找)
  • 我已经实现了一个高效(尽可能高效)的就地差异版本。它变得有点比这段代码复杂...
  • 很好(:我为实施+1。
【解决方案2】:

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

基本上,std::set_difference 等算法会将操作结果写入另一个对象。有趣的是,算法并不要求任何一个或两个操作数实际上都是std::set。算法的定义是:

效果:将[first1, last1) 范围内不存在于[first2, last2) 范围内的元素复制到以result 开头的范围内。对构造范围内的元素进行排序。

要求:结果范围不得与任何一个原始范围重叠。输入范围必须按相同的operator&lt; 排序。

返回:构造范围的结束。

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

有趣的区别是 C++ 版本适用于任何两个排序范围。在大多数语言中,您必须在访问 set Difference 算法之前将调用对象(左侧操作数)强制转换或转换为集合。

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

【讨论】:

    【解决方案3】:

    在 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++ );
            }
        }
    }
    

    该算法的性能将与参数的大小成线性关系,并且不需要额外的副本,因为它会修改第一个参数。

    【讨论】:

      【解决方案4】:

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

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

      我认为set_difference 效率更高,因为它可能只扫描两个集合一次

      【讨论】:

        【解决方案5】:

        Python set 是无序的,与有序的 std::set 相比,它更像是 C++ std::unordered_set。

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

        对于一种适用于有序集和无序集的更通用的解决方案,如果您要强制/保留 Python 集的“无序性”性质,则 Kiril Kirov 的算法应该是安全的选择。

        【讨论】:

          猜你喜欢
          • 2010-10-17
          • 2020-09-28
          • 2011-06-25
          • 2010-12-21
          • 2011-05-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多