2013-04-09 90 views
0

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

更新

其实我并不需要一个新的列表,我只是想在后面的代码列表的子集进行操作。我假设创建一个新列表是一个合理的方式来做到这一点(注意列表大小通常在50以下)。

+1

机会是你*不要*为输入*或*结果使用'std :: list'。在大多数情况下,'std :: vector'会使生活变得更简单。大多数算法在定义的范围内工作,这将允许您在正确的子集上进行操作,而无需复制。 – 2013-04-09 15:48:45

+0

@JerryCoffin我需要排序列表之前采取的子集,是否影响使用矢量的决定? – User 2013-04-09 15:54:31

+1

是的 - 它使矢量更好的选择。如果你只想要N个最大(或最小)的项目,你可以使用'std :: nth_element'来获取它们。 – 2013-04-09 16:43:21

回答

7
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)); 
+2

我看着,我不认为'std :: advance'会照顾到最后。至少这个标准只是让它推进迭代器。它没有提到检查。 – chris 2013-04-09 15:40:44

+0

为什么你不能简单地做'a.begin()+ n'? – 0x499602D2 2013-04-09 15:41:26

+1

@ 0x499602D2,这是一个双向迭代器。无论如何,'std :: next'都可以工作。 – chris 2013-04-09 15:41:41

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); 
+1

我想没有其他人同意迭代列表两次,当你可以很容易地避免它是这样愚蠢的悲观化。 – 2013-04-09 15:49:28

+0

@BenjaminLindley:你是什么意思?你是说这个比别人好还是不好?其他答案是否会导致列表被迭代两次? – User 2013-04-09 19:06:36

+3

@用户:是的,我的意思是说这个答案可能更有效率,但可能不是太多。其他答案使用'std :: next'来查找用于初始化第二个列表的结束迭代器。对于像'std :: vector'或'std :: deque'这样的容器,这将是一个便宜的操作,因为它们有随机访问迭代器。但对于'std :: list',由于它有双向迭代器,因此它需要遍历列表中的每个项目直到N.然后,当初始化第二个列表时,需要重新迭代。 – 2013-04-09 19:11:22

6
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))}; 
} 
+0

为了“返回”列表,我总是将空列表作为参数传递给函数。你在这里做的方式是否有成本影响,返回参数不会?作为一名C#程序员,我更喜欢这种方式,但大部分我读过的建议是通过return语句返回容器。 – User 2013-04-10 00:05:10

+1

@User请参阅http://stackoverflow.com/questions/753312/returning-large-objects-in-functions - 在所有现代编译器中,返回容器的效率与获取out参数一样高效。 – ecatmur 2013-04-10 07:48:04