例說資料結構&STL(二)——list

無鞋童鞋發表於2017-07-27

1 白話list(連結串列)
  list(連結串列,準確來說STL中該模板類是雙向連結串列,單向連結串列是slist(目前STL中還沒有單獨的容器實現),也是一種非常常用的資料結構,它和vector一樣是屬於STL中順序容器的一種。list中各個元素之間也存在一個線性的邏輯次序,但是與vector極其不同的是,各單元的實體地址可以任意,無需是連續的空間分配。
  相比較而言,vector的物理儲存次序與其邏輯關係必須嚴格的對應,所以獲取元素和size()統計個數操作均可以在常數級時間複雜度中完成,而插入(刪除)元素前不得不移動O(n)次後繼元素。而連結串列是一種動態儲存的結構,資料分散為一系列稱為節點的個體。節點之間通過指標相互聯絡與訪問。所以插入(刪除)元素只需要調整區域性少量的相關節點的指標指示。但是代價就是失去通過下標索引訪問各個成員的能力,只能通過r次迭代掃描來訪問第r個元素。也就是說list中刪除和插入元素是常數時間複雜度O(1),而索引與size()是線性時間複雜度O(n)。
2 STL中list實戰
 2.1 標頭檔案包含  
  STL容器list使用首先需要在程式開頭新增標頭檔案包含,這樣才可以直接呼叫list中各種類方法。

#include<list>

  其次一定要加上C++標準庫名稱空間std,一種方便形式是在檔案標頭檔案後面新增using namespace std;,這樣全文可以直接使用std下面定義的類與方法了。還可以每個實體前宣告諸如std::list形式,這樣稍顯複雜,但是如果程式中有自己定義的名稱空間就最好這樣宣告瞭。
 2.2 型別定義  
  這個算是一個技巧的介紹,有時候為了方便程式維護與以後修改,我們可以在程式頭型別定義對應的模板類為一個簡化名稱(一般大寫表示),如下:

typedef list<int> INTLIST;
typerdef vector<long> LONGVECTOR;
typerdef list<int>::iterator INTLISTITER;

INTLIST list_fir; //宣告一個list<int>物件

  這樣的好處就是一旦後面我們想用list<long>定義我們的變數,只需在此修改list<int>list<long>,從而不需要整個程式中修改了。此外取一個通俗易懂的名稱也會增加程式的通讀行。
 2.3 變數宣告  
  首先是最簡單的物件宣告,容器物件中為空,如下:

list<int> list_fir; //或者INTLIST list_fir,這裡不去簡寫

  下面我們再看看帶初始化的宣告方式是什麼樣子的。

list<int> list_sed(10,2);     // 申請10個空間,全部初始化為2

list<int> list_thd(list_fir); //拷貝構造

vector<int> vec(10,1);
list<int> list_for(vec.begin(),vec.end()); //拷貝vector一段資料初始化

 2.4 賦值操作 
  和vector一樣,同時格式和前面初始化其實也是一樣,引數分為下面幾種:

list_fir.assign(10,10086); //重新賦值10個10086

list_sed.assign(list_fir.begin(),list_fir.end()); 

 2.5 拼接操作 
  std::list中提供了兩種拼接的方法merge()和splice(),連結串列拼接實現如下:

list_fir.merge(list_sed); // 拼接加在list_fir上,而list_sed變為空

list_fir.splice(list_fir.begin(),list_sed); //在list_fir初始位置拼接上list_sed

 2.6 刪除操作 

list_fir.erase(++list_fir.begin()); //刪除第二個數

list_fir.erase(++list_fir.begin(),list_fir.end()); //刪除到只剩開頭一個數 

 2.7 插入操作 

list_fir.insert(list_fir.begin(),20); //首部插入一個20資料

list_fir.insert(list_fir.begin(),list_sed.begin(),list_sed.end());  // 插入整個list_sed在首部

 2.8 尾首刪除與新增操作 
  我們知道vector只提供了尾部刪除和新增的操作,而list不僅提供了尾部還提供了首部新增與刪除操作。

list_fir.push_back(110); //尾部增加一個110數
list_fir.push_front(1);  //首部增加一個1數

list_fir.pop_back();     //尾部刪除一個數
list_fir.pop_front();    //首部刪除一個數

 2.9 列印與遍歷操作
  前面介紹過vector可以通過下標索引每個元素,所以vector模板類中提供了operator[]運算子過載。而連結串列因為實體地址的不連續性,所以不可以通過下標訪問每個資料,切記沒有諸如list_fir[2]這樣的使用!我們只能通過迭代器來遍歷訪問每個資料。迭代器在vector介紹中已經說過,可以看成就是指標,使用的時候記得包含標頭檔案#include<iterator>但是需要注意一點,list中迭代器不能像vector或者deque中迭代器出現關係或者算數運算,例如list_fir.begin()+5這樣的操作,只允許iter++或者iter–這樣的迭代操作。這是因為vector和deque支援通過元素的位置實現隨機訪問,而list並不行。另外還包括反向迭代,更詳細介紹請另閱例說資料結構&STL(十二)——iterator
  list的元素訪問程式如下:

int data1 = list_fir.front(); // 第一個數

int data2 = list_fir.back();  // 最後一個數

list<int>::iterator iter;
for(iter=list_fir.begin();iter!=list_end();iter++)
{
    cout<<*iter<<endl;        //就像指標一樣*間接訪問每個數

}

 2.10 其他實用操作 

size_t list_fir.size(); // 統計list中資料個數

list_fir.unique();      // 連結串列中每個連續出現的數僅保留第一次出現的那個

if(!list_fir.empty())   // 判斷連結串列中是否為空
{
    list_fir.clear();   // 清空連結串列
}

list_fir.swap(list_sed);// 交換兩個連結串列所有資料

list_fir.sort();        // 增序排序整個連結串列

  這裡提醒一下unique()函式介面使用注意的地方,實際上它是和algorithm標頭檔案中unique實現一樣,對於連續相同元素僅保留一個,而對於間斷性相同元素,每個連續間斷都會儲存一個。如果想整個list中僅儲存相同的元素一個,那麼可以先排序再呼叫該unique()函式。
3 小結
  至此,我們將STL容器中list大部分方法介面介紹了一遍,相信大家有了一定的認識。前面也介紹了它的優勢,第一點可以節約空間,不用大片連續空間儲存;第二刪除與新增資料的方便,可以在常數級時間複雜度中完成工作。如果我們程式中需要頻繁刪除與新增資料(更確切的說是首部或者中間位置刪除或新增),我們最好是使用連結串列資料結構,這會大大提高我們程式的效能。
  以上是個人學習記錄,由於能力和時間有限,如果有錯誤望讀者糾正,謝謝!
  轉載請註明出處:http://blog.csdn.net/FX677588/article/details/76218306

相關文章