代码之家  ›  专栏  ›  技术社区  ›  rlbond

2组不相交的C++检验

  •  12
  • rlbond  · 技术社区  · 16 年前

    我知道STL有 set_difference ,但我需要知道2 set 它们是不相交的。我已经分析了我的代码,这让我的应用程序慢了很多。有没有一种简单的方法来判断两个集合是否不相交,或者我是否需要滚动我自己的代码?

    编辑:我也试过了 set_intersection 但它花了同样的时间。。。

    5 回复  |  直到 16 年前
        1
  •  17
  •   Graphics Noob    16 年前

    修改了hjhill的代码,通过去掉count()调用将复杂性降低了一倍O(logn)。

    template<class Set1, class Set2> 
    bool is_disjoint(const Set1 &set1, const Set2 &set2)
    {
        if(set1.empty() || set2.empty()) return true;
    
        typename Set1::const_iterator 
            it1 = set1.begin(), 
            it1End = set1.end();
        typename Set2::const_iterator 
            it2 = set2.begin(), 
            it2End = set2.end();
    
        if(*it1 > *set2.rbegin() || *it2 > *set1.rbegin()) return true;
    
        while(it1 != it1End && it2 != it2End)
        {
            if(*it1 == *it2) return false;
            if(*it1 < *it2) { it1++; }
            else { it2++; }
        }
    
        return true;
    }
    

    我已经编写并测试了这段代码,所以它应该是好的。

        2
  •  5
  •   Nikolai Fetissov    16 年前

    std::set 是一个已排序的容器,您可以比较设置的边界以查看它们是否可能相交,如果相交,则执行昂贵的STL操作。

    编辑:

    这里是捕蛤蜊变得严肃的地方。。。

    目前发布的所有代码都试图重新实现<算法>。以下是本文的要点 set_intersection :

    
      template<typename _InputIterator1, typename _InputIterator2,
               typename _OutputIterator>
        _OutputIterator
        set_intersection(_InputIterator1 __first1, _InputIterator1 __last1,
                         _InputIterator2 __first2, _InputIterator2 __last2,
                         _OutputIterator __result)
        {
          while (__first1 != __last1 && __first2 != __last2)
            if (*__first1 < *__first2)
              ++__first1;
            else if (*__first2 < *__first1)
              ++__first2;
            else
              {
                *__result = *__first1;
                ++__first1;
                ++__first2;
                ++__result;
              }
          return __result;
        }
    

    
      /// fake insert container
      template <typename T> struct intersect_flag
      {       
        typedef int iterator;
        typedef typename T::const_reference const_reference;
    
        bool flag_; // tells whether given sets intersect
    
        intersect_flag() : flag_( false ) {}
    
        iterator insert( iterator, const_reference )
        {       
          flag_ = true; return 0;
        }
      };
    
      // ...
      typedef std::set<std::string> my_set;
    
      my_set s0, s1;
      intersect_flag<my_set> intf;
    
      // ...        
      std::set_intersection( s0.begin(), s0.end(),
        s1.begin(), s1.end(), std::inserter( intf, 0 ));
    
      if ( intf.flag_ ) // sets intersect
      {
        // ...
    

    这样可以避免从原始集合复制对象,并允许重用STL算法。

        3
  •  3
  •   hjhill    16 年前

    你可以用 set_intersection 测试结果集是否为空,但我不知道这是否快得多。

    return false 一旦找到第一个相等的元素。不过,我不知道有什么现成的解决办法

    template<class Set1, class Set2> 
    bool is_disjoint(const Set1 &set1, const Set2 &set2)
    {
        Set1::const_iterator it, itEnd = set1.end();
        for (it = set1.begin(); it != itEnd; ++it)
            if (set2.count(*it))
                return false;
    
        return true;
    }
    

    不太复杂,应该做得很好。

    编辑: 如果您想要O(n)性能,请使用 轻微地

    template<class Set1, class Set2> 
    bool is_disjoint(const Set1 &set1, const Set2 &set2)
    {
        Set1::const_iterator it1 = set1.begin(), it1End = set1.end();
        if (it1 == it1End)
            return true; // first set empty => sets are disjoint
    
        Set2::const_iterator it2 = set2.begin(), it2End = set2.end();
        if (it2 == it2End)
            return true; // second set empty => sets are disjoint
    
        // first optimization: check if sets overlap (with O(1) complexity)
        Set1::const_iterator it1Last = it1End;
        if (*--it1Last < *it2)
            return true; // all elements in set1 < all elements in set2
        Set2::const_iterator it2Last = it2End;
        if (*--it2Last < *it1)
            return true; // all elements in set2 < all elements in set1
    
        // second optimization: begin scanning at the intersection point of the sets    
        it1 = set1.lower_bound(*it2);
        if (it1 == it1End)
            return true;
        it2 = set2.lower_bound(*it1);
        if (it2 == it2End)
            return true;
    
        // scan the (remaining part of the) sets (with O(n) complexity) 
        for(;;)
        {
            if (*it1 < *it2)
            {
                if (++it1 == it1End)
                    return true;
            } 
            else if (*it2 < *it1)
            {
                if (++it2 == it2End)
                    return true;
            }
            else
                return false;
        }
    }
    

    (进一步修改了图形Noob的修改,仅使用运算符<)

        4
  •  2
  •   ony    11 年前

    通过使用两个集合都已排序的事实,可以得到O(log(n))。只用 std::lower_bound 而不是将迭代器移动一次。

    enum Ordering { EQ = 0, LT = -1, GT = 1 };
    
    template <typename A, typename B>
    Ordering compare(const A &a, const B &b)
    {
        return
            a == b ? EQ :
            a < b ? LT : GT;
    }
    
    template <typename SetA, typename SetB>
    bool is_disjoint(const SetA &a, const SetB &b)
    {
        auto it_a = a.begin();
        auto it_b = b.begin();
        while (it_a != a.end() && it_b != b.end())
        {
            switch (compare(*it_a, *it_b))
            {
            case EQ:
                return false;
            case LT:
                it_a = std::lower_bound(++it_a, a.end(), *it_b);
                break;
            case GT:
                it_b = std::lower_bound(++it_b, b.end(), *it_a);
                break;
            }
        }
        return true;
    }
    
        5
  •  1
  •   Terry Mahaffey    16 年前

    使用std::set_intersection,查看输出是否为空。您可以先检查两个集合的范围(开始迭代器和结束迭代器覆盖的区域)是否重叠,但我怀疑集合交叉点可能已经这样做了。