如何在遍历时添加要设置的元素?

时间:2019-07-08 12:03:57

标签: c++ set

我们有一组S {1,10,100,1000,10000}。现在我们输入一个整数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合并。 但是由于原始数据集很大,因此会增加时间复杂度。 有优化吗?

3 个答案:

答案 0 :(得分:3)

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

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

for (auto it = S.rbegin(); it != S.rend(); ++it)
    S.insert(*it*x);

如果x为负,且集合仅包含正,则迭代顺序无关紧要。如果集合中可能包含负数,这将变得更具挑战性。


  

但是,由于原始数据集非常庞大,因此会增加时间复杂度。

将N个元素插入std::set中是O(N log N)。合并std::set为O(N log N)。合并方法不会增加渐近时间的复杂性。

但是,如果您要使用std::unordered_set,则合并方法通常为O(N)。但是在最坏的情况下,它仍然是O(N log N)。我建议对无序集合使用合并方法。

答案 1 :(得分:1)

如注释中所述,使用临时集最简单。在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 (auto it = s1.begin(); it != s1.end(); ++it)
   it = s.insert(*it*4).first;

为了减少限制,您可以使用以下更详细的循环:

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;

答案 2 :(得分:1)

您在这里。

#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;
}

程序输出为

1 10 100 1000 10000 
1 4 10 40 100 400 1000 4000 10000 40000 

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

  

9 insert和emplace成员不应影响以下内容的有效性:   容器的迭代器和引用,以及擦除成员   仅使迭代器和对已删除元素的引用无效。

或更通用的方法

#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;
}

程序输出为

-10000 -1000 -100 -10 -1 1 10 100 1000 10000 
-40000 -10000 -4000 -1000 -400 -100 -40 -10 -4 -1 1 4 10 40 100 400 1000 4000 10000 40000