1. 程式人生 > >C++中list用法詳解

C++中list用法詳解

1.關於list容器

list是一種序列式容器。list容器完成的功能實際上和資料結構中的雙向連結串列是極其相似的,list中的資料元素是通過連結串列指標串連成邏輯意義上的線性表,也就是list也具有連結串列的主要優點,即:在連結串列的任一位置進行元素的插入、刪除操作都是快速的。list的實現大概是這樣的:list的每個節點有三個域:前驅元素指標域、資料域和後繼元素指標域。前驅元素指標域儲存了前驅元素的首地址;資料域則是本節點的資料;後繼元素指標域則儲存了後繼元素的首地址。其實,list和迴圈連結串列也有相似的地方,即:頭節點的前驅元素指標域儲存的是連結串列中尾元素的首地址,list的尾節點的後繼元素指標域則儲存了頭節點的首地址,這樣,list實際上就構成了一個雙向迴圈鏈。

由於list元素節點並不要求在一段連續的記憶體中,顯然在list中是不支援快速隨機存取的,因此對於迭代器,只能通過“++”或“--”操作將迭代器移動到後繼/前驅節點元素處。而不能對迭代器進行+n或-n的操作,這點,是與vector等不同的地方。

我想把三個常用的序列式放在一起對比一下是有必要的:

vector : vector和built-in陣列類似,擁有一段連續的記憶體空間,能非常好的支援隨即存取,即[]操作符,但由於它的記憶體空間是連續的,所以在中間進行插入和刪除會造成記憶體塊的拷貝,另外,當插入較多的元素後,預留記憶體空間可能不夠,需要重新申請一塊足夠大的記憶體並把原來的資料拷貝到新的記憶體空間。這些影響了vector的效率,但是實際上用的最多的還是vector容器,建議大多數時候使用vector效率一般是不錯的。vector的用法解析可以參考本人的另一篇隨筆:

http://www.cnblogs.com/BeyondAnyTime/archive/2012/08/08/2627666.html

list:      list就是資料結構中的雙向連結串列(根據sgi stl原始碼),因此它的記憶體空間是不連續的,通過指標來進行資料的訪問,這個特點使得它的隨即存取變的非常沒有效率,因此它沒有提供[]操作符的過載。但由於連結串列的特點,它可以以很好的效率支援任意地方的刪除和插入。

deque: deque是一個double-ended queue,它的具體實現不太清楚,但知道它具有以下兩個特點:它支援[]操作符,也就是支援隨即存取,並且和vector的效率相差無幾,它支援在兩端的操作:push_back,push_front,pop_back,pop_front等,並且在兩端操作上與list的效率也差不多。

因此在實際使用時,如何選擇這三個容器中哪一個,應根據你的需要而定,具體可以遵循下面的原則
1. 如果你需要高效的隨即存取,而不在乎插入和刪除的效率,使用vector
2. 如果你需要大量的插入和刪除,而不關心隨即存取,則應使用list
3. 如果你需要隨即存取,而且關心兩端資料的插入和刪除,則應使用deque。

2.list中常用的函式

2.1list中的建構函式:

list() 宣告一個空列表;

list(n) 宣告一個有n個元素的列表,每個元素都是由其預設建構函式T()構造出來的

list(n,val) 宣告一個由n個元素的列表,每個元素都是由其複製建構函式T(val)得來的

list(n,val) 宣告一個和上面一樣的列表

list(first,last) 宣告一個列表,其元素的初始值來源於由區間所指定的序列中的元素

2.2 begin()和end():通過呼叫list容器的成員函式begin()得到一個指向容器起始位置的iterator,可以呼叫list容器的 end() 函式來得到list末端下一位置,相當於:int a[n]中的第n+1個位置a[n],實際上是不存在的,不能訪問,經常作為迴圈結束判斷結束條件使用。

2.3 push_back() 和push_front():使用list的成員函式push_back和push_front插入一個元素到list中。其中push_back()從list的末端插入,而 push_front()實現的從list的頭部插入。

2.4 empty():利用empty() 判斷list是否為空。

2.5 resize(): 如果呼叫resize(n)將list的長度改為只容納n個元素,超出的元素將被刪除,如果需要擴充套件那麼呼叫預設建構函式T()將元素加到list末端。如果呼叫resize(n,val),則擴充套件元素要呼叫建構函式T(val)函式進行元素構造,其餘部分相同。

2.6 clear(): 清空list中的所有元素。

2.7 front()和back(): 通過front()可以獲得list容器中的頭部元素,通過back()可以獲得list容器的最後一個元素。但是有一點要注意,就是list中元素是空的時候,這時候呼叫front()和back()會發生什麼呢?實際上會發生不能正常讀取資料的情況,但是這並不報錯,那我們程式設計序時就要注意了,個人覺得在使用之前最好先呼叫empty()函式判斷list是否為空。

2.8 pop_back和pop_front():通過刪除最後一個元素,通過pop_front()刪除第一個元素;序列必須不為空,如果當list為空的時候呼叫pop_back()和pop_front()會使程式崩掉。

2.9 assign():具體和vector中的操作類似,也是有兩種情況,第一種是:l1.assign(n,val)將 l1中元素變為n個T(val)。第二種情況是:l1.assign(l2.begin(),l2.end())將l2中的從l2.begin()到l2.end()之間的數值賦值給l1。

2.10 swap():交換兩個連結串列(兩個過載),一個是l1.swap(l2); 另外一個是swap(l1,l2),都可能完成連個連結串列的交換。

2.11 reverse():通過reverse()完成list的逆置。

2.12 merge():合併兩個連結串列並使之預設升序(也可改),l1.merge(l2,greater<int>()); 呼叫結束後l2變為空,l1中元素包含原來l1 和 l2中的元素,並且排好序,升序。其實預設是升序,greater<int>()可以省略,另外greater<int>()是可以變的,也可以不按升序排列。

看一下下面的程式:

複製程式碼
 1 #include <iostream>
 2 #include <list>
 3 
 4 using namespace std;
 5 
 6 int main()
 7 {
 8     list<int> l1;
 9     list<int> l2(2,0);
10     list<int>::iterator iter;
11     l1.push_back(1);
12     l1.push_back(2);
13     l2.push_back(3);
14     l1.merge(l2,greater<int>());//合併後升序排列,實際上預設就是升序
15     for(iter = l1.begin() ; iter != l1.end() ; iter++)
16     {
17         cout<<*iter<<" ";
18     }
19     cout<<endl<<endl;
20     if(l2.empty())
21     {
22         cout<<"l2 變為空 !!";
23     }
24     cout<<endl<<endl;
25     return 0;
26 }
複製程式碼

執行結果:

2.13 insert():在指定位置插入一個或多個元素(三個過載):

l1.insert(l1.begin(),100); 在l1的開始位置插入100。

l1.insert(l1.begin(),2,200); 在l1的開始位置插入2個100。

l1.insert(l1.begin(),l2.begin(),l2.end());在l1的開始位置插入l2的從開始到結束的所有位置的元素。

2.14 erase():刪除一個元素或一個區域的元素(兩個過載)

l1.erase(l1.begin()); 將l1的第一個元素刪除。

l1.erase(l1.begin(),l1.end()); 將l1的從begin()到end()之間的元素刪除。