【问题标题】:Get first N elements of std::list?获取 std::list 的前 N ​​个元素?
【发布时间】:2013-04-09 15:35:53
【问题描述】:

如果 N >= 列表大小(并且也处理 N = 0),获取作为 std::list 的前 N ​​个元素或整个列表的新列表的正确且安全的方法是什么?

更新

其实我并不一定需要新的列表,我只是想在后续代码中对列表的子集进行操作。我认为创建一个新列表是一种合理的方法(注意列表大小通常小于 50)。

【问题讨论】:

  • 您有可能不想想对输入结果使用std::list。在大多数情况下,std::vector 会让生活变得更简单。大多数算法都在定义的范围内工作,这将允许您对正确的子集进行操作而无需复制。
  • @JerryCoffin 我需要在获取子集之前对列表进行排序,这会影响使用向量的决定吗?
  • 是的——它使矢量成为更好的选择。如果只想要N个最大(或最小)的物品,可以使用std::nth_element来获取。

标签: c++ visual-c++-2010 stdlist


【解决方案1】:
std::list<int> a;
size_t n = 13;
auto end = std::next(a.begin(), std::min(n, a.size()));

创建一个包含第一个列表的前 n 个元素的新列表:

std::list<int> b(a.begin(), end);

或填充现有列表:

std::list<int> b;
std::copy(a.begin(), end, std::back_inserter(b));

【讨论】:

  • 我看了看,我不认为std::advance 会照顾到最后。至少标准只是让它推进迭代器。它没有提到检查。
  • 你为什么不能简单地做a.begin() + n
  • @0x499602D2,它是一个双向迭代器。 std::next 无论如何都可以使用。
  • 由于 OP 谈到“新列表”,因此使用两个迭代器构造函数构造 b 可能更有意义。
  • 你为什么用operator+代替std::next来破坏你的代码,它不能用于列表迭代器?
【解决方案2】:
template<typename T>
std::list<T> first_n(const std::list<T> &in, std::size_t n) {
    return std::list<T> out{in.begin(),
      std::next(in.begin(), std::min(in.size(), n))};
}

【讨论】:

  • 为了“返回”一个列表,我总是将空列表作为参数传递给函数。您在这里执行的方式是否具有返回参数没有的成本影响?作为一名 C# 程序员,我更喜欢这种方式,但我读过的大部分内容都建议不要通过 return 语句返回容器。
  • @User 请参阅stackoverflow.com/questions/753312/… - 在所有现代编译器中返回容器将与获取输出参数一样有效。
【解决方案3】:
// list<int> input;
list<int> output;
for (list<int>::const_iterator i = input.begin(); i != input.end() && N > 0; ++i, --N)
    output.push_back(*i);

【讨论】:

  • 我想没有其他人同意迭代列表两次,而你可以像这样轻松避免它是愚蠢的悲观。
  • @BenjaminLindley:你是什么意思?您说这是比其他答案更好还是不如其他答案?其他答案是否会导致列表被迭代两次?
  • @User:是的,我是说这个答案可能更有效,尽管可能不是很多。其他答案使用std::next 来查找用于初始化第二个列表的结束迭代器。对于像std::vectorstd::deque 这样的容器,这将是一个便宜的操作,因为它们具有随机访问迭代器。但是对于std::list,由于它有双向迭代器,它需要遍历列表中的每一项,直到N。然后当你初始化第二个列表时,你需要再次迭代。
【解决方案4】:

在你的问题中注意到说

其实我不一定需要新的列表,我只是想操作 后续代码中列表的子集

Form C++17 可以使用std::for_each_n

例如,让列表中的前 N ​​(4) 个数字平方。

示例1:就地修改:

    std::list<int> nums{ 1,2,3,4,5,6,7,8 };

    //MAKE NOTE OF SENDING ARGUMENT AS A REFERENCE (int& num)
    std::for_each_n(nums.begin(), 4, [](int& num) { num = num * num; });

    //PRINT
    for (auto n : nums)
        std::cout << n << "  ";

示例 2:修改并将它们放在不同的列表中:

    std::list<int> nums2{ 1,2,3,4,5,6,7,8 };
    std::list<int> newNums;

    //MAKE NOTE OF CAPTURE [&}, newNums EXPOSED INSIDE LAMBDA.
    std::for_each_n(nums2.begin(), 4, [&](int num) {newNums.push_back(num * num); });
    
    //PRINT
    for (auto n : newNums)
        std::cout << n << "  ";

还有一个重载可用于指定执行策略,需要包含“执行”标头。所以下面的代码使用并行执行策略执行,对于并行处理巨大的列表很有用。

std::for_each_n(std::execution::par,nums.begin(), 4, [](int& num) { num = num * num; });

【讨论】:

    猜你喜欢
    • 2014-09-17
    • 2010-12-21
    • 2011-04-12
    • 2011-02-11
    • 2017-02-27
    • 1970-01-01
    • 2022-07-01
    • 2011-11-13
    • 1970-01-01
    相关资源
    最近更新 更多