C++ 如何在遍历集合时向集合添加元素?

C++ 如何在遍历集合时向集合添加元素?,c++,set,C++,Set,我们有一个集合S{1,10100100010000}。现在我们输入一个整数x,比如x=4 现在我们必须将集合乘积的每个元素与x一起添加到集合本身。所以最后 S={1,10,100,1000,10000,4,40,400,4000,40000} [S最初不限于5个条目] 我们只需要访问集合中的初始元素。 我试过这样的方法: for(auto i=s.begin();i!=s.end();i++) { s.insert((*i)*x); } 随着集合的大小不断增加,这并没有给出期望的结果

我们有一个集合S{1,10100100010000}。现在我们输入一个整数x,比如x=4

现在我们必须将集合乘积的每个元素与x一起添加到集合本身。所以最后

S={1,10,100,1000,10000,4,40,400,4000,40000}
[S最初不限于5个条目]

我们只需要访问集合中的初始元素。 我试过这样的方法:

for(auto i=s.begin();i!=s.end();i++)
{
    s.insert((*i)*x);
}
随着集合的大小不断增加,这并没有给出期望的结果

我尝试的另一种方法是将所有倍数*I*x存储在另一个临时集合/向量中,然后将其与s合并。 但由于原始数据量巨大,使得时间复杂度恶化。
有任何优化吗?

如评论中所述,使用临时集是最简单的。使用C++17,您可以使用std::set::merge和强制转换为右值的临时集:

#include <algorithm>

std::set<int> s2;

std::transform(s1.cbegin(), s1.cend(), std::inserter(s2, s2.end()),
      [](int orig){ return 4*orig; });

s1.merge(std::move(s2));
为了减少限制,可以使用更详细的循环:

for (std::set<int>::iterator it1 = s.begin(), it2; it1 != s.end();
        it1 = std::next(it1) == it2 ? std::next(it1, 2) : it2)
   it2 = s.insert(*it1*4).first;

正如注释中提到的,使用临时集是最简单的。使用C++17,您可以使用std::set::merge和强制转换为右值的临时集:

#include <algorithm>

std::set<int> s2;

std::transform(s1.cbegin(), s1.cend(), std::inserter(s2, s2.end()),
      [](int orig){ return 4*orig; });

s1.merge(std::move(s2));
为了减少限制,可以使用更详细的循环:

for (std::set<int>::iterator it1 = s.begin(), it2; it1 != s.end();
        it1 = std::next(it1) == it2 ? std::next(it1, 2) : it2)
   it2 = s.insert(*it1*4).first;
给你

#include <iostream>
#include <set>
#include <iterator>

std::set<int> & update( std::set<int> &s, int value )
{
    for ( auto it = rbegin( s ); it != rend( s ); ++it )
    {
        s.insert( *it * value );
    }

    return s;
}

int main() 
{
    std::set<int> s = { 1, 10, 100, 1000, 10000 };

    for ( const auto &value : s )
    {
        std::cout << value << ' ';
    }
    std::cout << '\n';

    for ( const auto &value : update( s, 4 ) )
    {
        std::cout << value << ' ';
    }
    std::cout << '\n';

    return 0;
}

根据C++ 20标准22.2.6关联容器

9插入件和安放件不得影响插入件和安放件的有效性 迭代器和容器引用,以及擦除成员 仅使迭代器和对已删除元素的引用无效

或者更一般的方法

#include <iostream>
#include <set>
#include <iterator>

std::set<int> & update( std::set<int> &s, int value )
{
    auto partition = s.lower_bound( 0 );

    for ( auto it = rbegin( s ); it != std::set<int>::reverse_iterator( partition ); ++it )
    {
        s.insert( *it * value );
    }

    for ( auto it  = begin( s ); it != partition; ++it )
    {
        s.insert( *it * value );
    }


    return s;
}

int main() 
{
    std::set<int> s = { -10000, -1000, -100, -10, -1, 1, 10, 100, 1000, 10000 };

    for ( const auto &value : s )
    {
        std::cout << value << ' ';
    }
    std::cout << '\n';

    for ( const auto &value : update( s, 4 ) )
    {
        std::cout << value << ' ';
    }
    std::cout << '\n';

    return 0;
}
给你

#include <iostream>
#include <set>
#include <iterator>

std::set<int> & update( std::set<int> &s, int value )
{
    for ( auto it = rbegin( s ); it != rend( s ); ++it )
    {
        s.insert( *it * value );
    }

    return s;
}

int main() 
{
    std::set<int> s = { 1, 10, 100, 1000, 10000 };

    for ( const auto &value : s )
    {
        std::cout << value << ' ';
    }
    std::cout << '\n';

    for ( const auto &value : update( s, 4 ) )
    {
        std::cout << value << ' ';
    }
    std::cout << '\n';

    return 0;
}

根据C++ 20标准22.2.6关联容器

9插入件和安放件不得影响插入件和安放件的有效性 应删除对容器和成员的引用 仅使迭代器和对已删除元素的引用无效

或者更一般的方法

#include <iostream>
#include <set>
#include <iterator>

std::set<int> & update( std::set<int> &s, int value )
{
    auto partition = s.lower_bound( 0 );

    for ( auto it = rbegin( s ); it != std::set<int>::reverse_iterator( partition ); ++it )
    {
        s.insert( *it * value );
    }

    for ( auto it  = begin( s ); it != partition; ++it )
    {
        s.insert( *it * value );
    }


    return s;
}

int main() 
{
    std::set<int> s = { -10000, -1000, -100, -10, -1, 1, 10, 100, 1000, 10000 };

    for ( const auto &value : s )
    {
        std::cout << value << ' ';
    }
    std::cout << '\n';

    for ( const auto &value : update( s, 4 ) )
    {
        std::cout << value << ' ';
    }
    std::cout << '\n';

    return 0;
}
由于std::set是有序的,并且迭代器不会因插入而失效,因此只要不插入到仍有待迭代的范围内,就可以在迭代时简单地插入

如果我们可以假设所有数字都是正数,那么我们可以反向迭代,因为乘法的结果总是大于输入:

for (auto it = S.rbegin(); it != S.rend(); ++it)
    S.insert(*it*x);
若x是负数,集合只包含正数,那个么迭代的顺序就无关紧要了。如果集合可能包含负数,这将变得更具挑战性

但由于原始数据量巨大,使得时间复杂度恶化

在std::set中插入N个元素在log N上。合并std::set在log N上。合并方法不会恶化渐近时间复杂性

但是,如果要使用std::unordered_集,则合并方法在一般情况下是正确的。然而,在最坏的情况下,它仍然在日志N上。我建议对无序集使用合并方法。

因为std::set是有序的,迭代器不会因插入而失效,所以只要不插入到仍有待迭代的范围内,就可以在迭代时插入

如果我们可以假设所有数字都是正数,那么我们可以反向迭代,因为乘法的结果总是大于输入:

for (auto it = S.rbegin(); it != S.rend(); ++it)
    S.insert(*it*x);
若x是负数,集合只包含正数,那个么迭代的顺序就无关紧要了。如果集合可能包含负数,这将变得更具挑战性

但由于原始数据量巨大,使得时间复杂度恶化

在std::set中插入N个元素在log N上。合并std::set在log N上。合并方法不会恶化渐近时间复杂性


但是,如果要使用std::unordered_集,则合并方法在一般情况下是正确的。然而,在最坏的情况下,它仍然在日志N上。我建议对无序集使用合并方法。

添加到临时集,然后合并原始集和临时集,@RichardCritten我提到过,我已经尝试过这种方法。但是,由于原始集合非常庞大,时间复杂度进一步恶化。正确的解决方案需要了解std::set的某些属性,以便能够在,不使用临时集。它会使时间复杂度恶化,它在合并时处于启用状态,但在多次插入的日志n上处于启用状态。@std::set::merge的Jarod42复杂度要求在日志n上。但渐近复杂度没有恶化的点仍然存在。添加到临时集,然后合并原始和临时集,@我提到的理查德克里滕,我已经尝试过这种方法。但是,由于原始集合非常庞大,时间复杂度进一步恶化。正确的解决方案需要了解std::set的某些属性,以便能够在,不使用临时集。它会使时间复杂度恶化,它在合并时处于启用状态,但在多次插入的日志n上处于启用状态。@std::set::merge的Jarod42复杂度要求在日志n上。但渐近复杂度没有恶化的点仍然存在。这在没有临时对象的情况下是可行的,使用某种利用std::set的方法

属性。@像这样的SamVarshavchik?它严重依赖于假设,不确定这是否是一个好的解决方案。如果原始集合包含{1,2,3},则第二个循环将失败。它将只添加4,然后终止。正确的解决方案将适用于集合中的任何值。@SamVarshavchik然后请共享正确的解决方案,而不是让我们guess@Justin我想这应该可以。但这很难看。如果没有临时对象,使用某种利用std::set属性的方法,这是可行的。@SamVarshavchik像这样?它严重依赖于假设,不确定这是否是一个好的解决方案。如果原始集合包含{1,2,3},则第二个循环将失败。它将只添加4,然后终止。正确的解决方案将适用于集合中的任何值。@SamVarshavchik然后请共享正确的解决方案,而不是让我们guess@Justin我想这应该可以。但这很难看。埃罗里卡的答案也正确地处理了负数。@Jarod42,除了原始集合中的负数。也可以处理它们,但似乎并不简单。@Jarod42实际上,即使set为正,x为负,迭代顺序也无关紧要,因为所有元素都是在迭代范围之前插入的,所以我的方法是毫无意义的。eerorika的答案也正确地处理了负数。@Jarod42,除了原始集合中的负数。也可以处理它们,但似乎并不简单。@Jarod42实际上,即使set是正的,x是负的,迭代的顺序也不重要,因为所有元素都是在迭代范围之前插入的,所以我的方法是没有意义的。我觉得可以检入使其工作的必要属性,可能对std::is_排序使用自定义比较运算符。。。这将是一个很好的断言。您错过了一个明显的优化,使用了一个重载的插入,它接受了一个额外的提示迭代器参数。你已经有了,就在这里。新值将始终插入到当前迭代器位置之后。@SamVarshavchik新值将始终插入到当前迭代器位置之后。为什么?考虑例如:s= {1,1,2}和x=4。我看不到保证。我觉得这项工作所需的属性可以签入,可能使用std::is_sorted的自定义比较运算符。。。这将是一个很好的断言。您错过了一个明显的优化,使用了一个重载的插入,它接受了一个额外的提示迭代器参数。你已经有了,就在这里。新值将始终插入到当前迭代器位置之后。@SamVarshavchik新值将始终插入到当前迭代器位置之后。为什么?考虑例如:s= {1,1,2}和x=4。我看不出保证。