就地C ++集合交集

时间:2009-11-20 22:01:12

标签: c++ stl set

在C ++中交叉两个集合的标准方法是执行以下操作:

std::set<int> set_1;  // With some elements
std::set<int> set_2;  // With some other elements
std::set<int> the_intersection;  // Destination of intersect
std::set_intersection(set_1.begin(), set_1.end(), set_2.begin(), set_2.end(), std::inserter(the_intersection, the_intersection.end()));

我如何进行就地设置交叉路口?也就是说,我希望set_1具有对set_intersection的调用结果。显然,我可以做一个set_1.swap(the_intersection),但这比在原地相交的效率低很多。

3 个答案:

答案 0 :(得分:12)

我想我已经明白了:

std::set<int>::iterator it1 = set_1.begin();
std::set<int>::iterator it2 = set_2.begin();
while ( (it1 != set_1.end()) && (it2 != set_2.end()) ) {
    if (*it1 < *it2) {
        set_1.erase(it1++);
    } else if (*it2 < *it1) {
        ++it2;
    } else { // *it1 == *it2
            ++it1;
            ++it2;
    }
}
// Anything left in set_1 from here on did not appear in set_2,
// so we remove it.
set_1.erase(it1, set_1.end());

有人看到任何问题吗?似乎是两组大小的O(n)。根据{{​​3}},std :: set erase(position)是分摊的常量,而erase(first,last)是O(log n)。

答案 1 :(得分:4)

您可以轻松浏览set_1,检查每个元素以查看它是否存在于set_2中,如果不存在则将其删除。由于集合已经过排序,您可以在线性时间内对它们进行比较,并使用迭代器删除元素为amortized constant time。我不会指望它比你开始时更有效率,如果对你来说重要的是基准测试是明智的。

答案 2 :(得分:1)

这不是直接回答问题,但也许有人觉得这很有帮助。

对于std::vector,使用标准算法set_1.begin()作为输出迭代器是安全的。请注意,set_2 可以是,而不仅仅是std::vector

std::vector<int> set_1;  // With some elements
std::vector<int> set_2;  // With some other elements
auto end = std::set_intersection(
                     set_1.begin(), set_1.end(), 
                     set_2.begin(), set_2.end(), 
                     set_1.begin() // intersection is written in set_1
                    );
set_1.erase(end, set_1.end()); // erase redundant elements