C++ 赋值vs std::交换、合并并在单独的对象中保留重复项

C++ 赋值vs std::交换、合并并在单独的对象中保留重复项,c++,algorithm,merge,set,C++,Algorithm,Merge,Set,假设我有两个std::sets。第一个选项,old\u options,需要与包含在new\u options中的其他选项合并。我不能只使用std::merge(当然,我可以,但不仅如此),因为我还检查double并相应地警告用户。为此,我有 void merge_options( set<string> &old_options, const set<string> &new_options ) { // find duplicates and

假设我有两个
std::set
s。第一个选项,
old\u options
,需要与包含在
new\u options
中的其他选项合并。我不能只使用
std::merge
(当然,我可以,但不仅如此),因为我还检查double并相应地警告用户。为此,我有

void merge_options( set<string> &old_options, const set<string> &new_options )
{
    // find duplicates and create merged_options, a stringset containing the merged options
    // handle duplicated the way I want to
    // ...
    old_options = merged_options;
}
还是我的任务

  • 是否有比连续调用
    std::set_intersection
    std::set_union
    更好的方法来过滤重复项并返回合并的
    set
    ,以检测重复项并合并
    set
    s?我知道这比一次遍历和同时执行两次遍历要慢,但这些集合很小(性能并不重要),我更信任标准,而不是我自己

  • 考虑到(如您所述)性能在这里并不重要,我将使用赋值和两次传递算法。它更简单,更容易理解;只有当你真的需要交换的好处时,才值得使用交换这样的“技巧”

    编写自己的算法并不是一件坏事,但再说一次,除非你能真正利用它提供的好处,否则我不会费心去做。

    这有什么问题吗

    void merge_options( set<string> &old_options, const set<string> &new_options )
    {
        for (set<string>::iterator i = new_options.begin();
             i != new_options.end(); ++i)
            if (old_options.find(*i) != old_options.end())
                warn_duplicate(*i);
            else
                old_options.insert(*i);
    }
    
    无效合并选项(设置和旧选项、常量设置和新选项)
    {
    for(set::iterator i=new_options.begin();
    i!=新的_选项.end();++i)
    if(old_options.find(*i)!=old_options.end())
    警告重复(*i);
    其他的
    旧选项。插入(*i);
    }
    

    这是一个简单的O(mlgn)算法,其中m=
    新选项.size()
    和n=
    旧选项.size()
    这部分是对拉斯曼的回答。有一个
    remove\u copy\u if
    算法将他的
    for
    循环封装到一个函数中。下面使用C++0x lambda作为谓词

    void merge_options( set<string> &old_options, const set<string> &new_options )
    {
        remove_copy_if(
            new_options.begin(),
            new_options.end(),
            inserter(old_options, old_options.end()),
            [&](const string &s) {
                return (old_options.count(s)) ? warn_duplicate(s), true : false;
            }
        );
    }
    
    无效合并选项(设置和旧选项、常量设置和新选项)
    {
    如果需要,请删除\u复制\u(
    新的\u选项。开始(),
    新的_选项。结束(),
    插入器(旧选项,旧选项.end()),
    [&](常量字符串&s){
    返回(旧选项计数)?警告重复,真:假;
    }
    );
    }
    
    simple,是的,但在性能上并不相同(如果忽略因子2,这在big-O中应该不重要)。我在脑海中有一个同步遍历,它本质上是O(log(max(m,n)))。这将真正获得性能,但是正如杰瑞所说,这里更复杂,也许没有用处。我只是确保我没有错过C++库函数。@ RuNVB:同时遍历将是O(max(m,n))。,我想。这个版本和你的版本的区别是1)更少的分配和复制(假设C++03),2)我认为我的版本更清晰。我不认为标准库有较短的方法来实现这一点,但我必须承认,
    一直让我感到惊讶。啊,是的,
    log
    用于二进制搜索,愚蠢的我
    =s
    @高炉也有一个不错的。(而且
    也让我感到惊讶,因此我提出了一个问题)是的,同样复杂。lambda介绍人中的
    &
    是做什么的?我还不熟悉lambda。@larsmans:这是一个capture子句,允许lambda访问封闭范围内的变量。
    &
    通过引用访问所有捕获的变量。在这种情况下,它允许lambda查看(和修改)旧的_选项集。
    void merge_options( set<string> &old_options, const set<string> &new_options )
    {
        remove_copy_if(
            new_options.begin(),
            new_options.end(),
            inserter(old_options, old_options.end()),
            [&](const string &s) {
                return (old_options.count(s)) ? warn_duplicate(s), true : false;
            }
        );
    }