2013-04-09 164 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