就地C++;集合交点 < > 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())); std::set\u 1;//有一些因素 std::set_2;//还有其他一些因素 std::设置_交点;//交集目的地 std::set_intersection(set_1.begin(),set_1.end(),set_2.begin(),set_2.end(),std::inserter(the_intersection,the_intersection.end());

就地C++;集合交点 < > 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())); std::set\u 1;//有一些因素 std::set_2;//还有其他一些因素 std::设置_交点;//交集目的地 std::set_intersection(set_1.begin(),set_1.end(),set_2.begin(),set_2.end(),std::inserter(the_intersection,the_intersection.end());,c++,stl,set,C++,Stl,Set,我将如何进行原地交叉?也就是说,我希望set_1具有调用set_intersection的结果。显然,我可以只做一个set\u 1.交换(交叉点),但这比原地交叉效率低得多。您可以轻松地通过set\u 1,检查每个元素是否存在于set\u 2中,如果不存在,则将其删除。由于集合是经过排序的,所以可以在线性时间内对它们进行比较,并且可以使用迭代器删除元素。我不会指望它比你开始时更有效,但是,如果它对你来说很重要,基准测试将是明智的。我想我已经做到了: std::set<int>::it

我将如何进行原地交叉?也就是说,我希望set_1具有调用set_intersection的结果。显然,我可以只做一个
set\u 1.交换(交叉点)
,但这比原地交叉效率低得多。

您可以轻松地通过
set\u 1
,检查每个元素是否存在于
set\u 2
中,如果不存在,则将其删除。由于集合是经过排序的,所以可以在线性时间内对它们进行比较,并且可以使用迭代器删除元素。我不会指望它比你开始时更有效,但是,如果它对你来说很重要,基准测试将是明智的。

我想我已经做到了:

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());
std::set::iterator it1=set_1.begin();
std::set::iterator it2=set_2.begin();
而((it1!=set_1.end())&&(it2!=set_2.end()){
如果(*it1<*it2){
设置_1.擦除(it1++);
}否则,如果(*it2<*it1){
++it2;
}否则{//*it1==*it2
++it1;
++it2;
}
}
//从现在起,在集合1中留下的任何内容都不会出现在集合2中,
//所以我们删除它。
set_1.erase(it1,set_1.end());

有人看到什么问题吗?似乎是O(n)上的两套大小。根据,std::set erase(position)是摊销常数,而erase(first,last)是O(logn)。

这并不能直接回答这个问题,但也许有人会觉得这很有用

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

所以我最初提出的是错误的,但在主要的STL实现中是可行的解决方案。如果您希望安全起见,并且不需要额外的分配,那么将您选择的实现复制到您的代码库中,并使用它而不是
std::set_intersection
。我真的不明白这种限制的原因,如果你知道答案,请评论

在所有方面都是正确的。在我看来,直觉上,应该可以一次迭代两个集合,并在适当的位置进行交集。我只是不知道怎么做。平衡二叉树中单个元素的擦除操作在
O(logn)
@ThomasMcLeod中运行不,它是摊销常数。当我写答案的时候,我不知道这一点,但我现在知道了,我已经更新以反映这一点。有趣的是,标准要求稀疏树平衡。我本以为这是一个实现细节。continues是多余的,我会重新安排为
if(*it1Right!因为它是if-else-if,等等。我想检查以下条件。谢谢,我会编辑答案。
set_1.擦除(it1++)
对于某些容器(如vector)是不正确的,即使它在您的情况下是有效的。您应该使用对所有容器都有效的
it1=set_1.erase(it1)
。在这种情况下,it1是否正确递增?也就是说,set_1.erase(it1)增量迭代器?<代码>::擦除< /> >不会增加迭代器,但是许多实现将递增迭代器作为返回值。但是我不认为它是原始C++规范的一部分,例如,您是否有安全的引用?我想这样做,但我找到的唯一来源。(所有这些都是非权威性的)说“结果范围不能与任何一个输入范围重叠”。很好的回答!严格来说你是对的,cppreference页面明确禁止它。我不知道这个限制的来源。我派生了“安全性”从可能的实现和接下来的两个事实来看。集合交集的大小不超过
set_1
的大小,并且在过程中没有迭代器无效。据我所知,违反规则的唯一副作用是在过程中
set_1
元素的自赋值。参考文献引用了stan的一段不幸的是,dard(25.4.5.3)完全正确,因此不能依赖于。一种解决方法是将标准实现复制到您的代码库中,这将保证它。
The resulting range shall not overlap with either of the original ranges