[C ++的STL筆記---有搬運】

1. list  

      list 由雙向鏈表(doubly linked list)實現而成,元素也存放在堆中,具有雙鏈表結構;每個元素都是放在一塊內存中,其內存空間可以是不連續的,維護一對前向和後向指針,通過指針來進行數據的訪問,因此支持前向/後向遍歷。這個特點使得它的隨機存取變得非常沒有效率,因此它沒有提供 [] 操作符的重載。但是由於鏈表的特點,它可以很有效率的支持任意地方的插入和刪除操作。

list 與 vector、deque 的用法基本一致,除了以下幾處不同:

  • list 爲雙向迭代器,故不支持it+=1
  • list 不支持下標訪問和at方法訪問。

list容器的迭代器不支持加減等數術運算,因爲list容器不支持像vector容器那樣的快速隨機訪問,只能依次訪問每個元素。

數據結構的區別

vector

  vector與數組類似,擁有一段連續的內存空間,並且起始地址不變。便於隨機訪問,時間複雜度爲O(1),但因爲內存空間是連續的,所以在進入插入和刪除操作時,會造成內存塊的拷貝,時間複雜度爲O(n)。

  此外,當數組內存空間不足,會採取擴容,通過重新申請一塊更大的內存空間進行內存拷貝。所以對開始和中間進行操作會引起內存重新分配,複雜度較大,而對末尾進行操作(push_back\pop)效率較好,https://blog.csdn.net/Africa_South/article/details/87875863

List

 list總結不錯的博客:https://blog.csdn.net/Africa_South/article/details/87897561 

      list底層是由雙向鏈表實現的,因此內存空間不是連續的。根據鏈表的實現原理,List查詢效率較低,時間複雜度爲O(n),不支持下標訪問,但插入和刪除效率較高。只需要在插入的地方更改指針的指向即可,不用移動數據。

 

迭代器支持不同

異:vector中,iterator支持 ”+“、”+=“,”<"等操作。而list中則不支持。

同:vector<int>::iterator和list<int>::iterator都重載了 “++ ”操作。

參考鏈接https://www.cnblogs.com/linuxAndMcu/p/10260627.html

其他容器鏈接:https://www.cnblogs.com/skyfsm/p/6934246.html


2. vector

   vector存儲是連續的,支持高效的隨機訪問和在尾端插入和刪除,在其他插入刪除操作效率較低


3. 比較:

  vector V.S. list V.S. deque:
  a、若需要隨機訪問操作,則選擇vector;
  b、若已經知道需要存儲元素的數目, 則選擇vector;
  c、若需要隨機插入/刪除(不僅僅在兩端),則選擇list;
  d、如果經常進行添加和刪除操作並且不經常隨機訪問的話,使用list;
  e、若既需要隨機插入/刪除,又需要隨機訪問,則需要在vector與list間做個折中;

 

4. 容易的erase (搬運)

 

容器按內存分配方式可以分爲鏈表容器和數組容器。

      所謂的鏈表容器指的是一種表現方式,包括list、slist等這樣基於節點的容器(動態分配內存塊)和set、map、multiset、multimap等關聯容器(平衡樹實現),而數組容器指的是在一塊連續的內存上保存元素的連續內存容器,比如vector、deque、string等。

     鏈表容器以list爲例,當執行container.erase(it)時,確實第一個滿足條件的元素刪除了,但這時it指針已經被刪除了,它也不指向任何元素了,所以也只能到此爲止了,也就是說上面的代碼對於鏈表容器來說只能正確刪除第一個滿足條件的元素,針對這個問題我們首先想到的就是在刪除指針之前,給其做個備份。

將這個臨時變量直接建立在erase實現裏,這樣做更簡潔,也顯得專業些。

list<int>::iterator it; 
for (it = lt.begin(); it != lt.end(); ) 
{
    if (*it % 2 == 0)
      lt.erase(it++); //這裏是關鍵
    else
      ++it;
}

 list與vector中的erase用法相同,它們的 erase 函數會返回指向下一個元素的迭代器,因此在遍歷時,只需要 it = v.erase(it)重新把下一個元素指針賦給it 即可。如示例代碼:

list<int>::iterator it; 
for (it = lt.begin(); it != lt.end(); ) {
  if (*it % 2 == 0)
    it = lt.erase(it);//自動返回下一個元素的地址,不用再主動前移指針
  else
    ++it;
}

 map與set中的erase用法相同,它們的 erase 函數返回值是 void,調用 erase 之後,當前迭代器會失效,無法再用於獲取下一個迭代器。因此需要 erase 之前就獲取指向下一個元素的迭代器。https://blog.csdn.net/zhaojunwuiris/article/details/80647653

map<int, int>::iterator it = m.begin();  
for (; it != m.end();)  
{  
    if (it->fist == 10) {  
        m.erase(it++);  
    }  
    else {  
        ++it;  
    }  
}

 5 vector提高效率方法 https://www.cnblogs.com/simonote/p/9265374.html ;                             

                                         https://blog.csdn.net/qq_37037492/article/details/86568290

                                         https://www.cnblogs.com/chhuach2005/p/3627011.html

    emplace_back 代替pusk_back,濫用push_back是可怕的性能殺手;

    shrink_to_fit() 釋放 vector 用內存,erase和clear不能做到;

    先reserve在push_back,

    儘量提前分配好內存;

finally 選擇容器類型的法則:https://blog.csdn.net/qq_36770641/article/details/88667720

1.如果程序是隨機範圍容器內的元素,則選擇vector/deque 容器

2.如果程序必須在容器的中間位置插入元素,則選擇list

3.如果程序是在容器首尾插入元素,則選擇deque

4.如果只需要在讀取輸入時在容器中間插入元素,然後隨機訪問,

則考慮在 輸入時 將元素 讀入list,接着 對list 重新排序

使其適合順序訪問,然後將 排序後的list 容器 複製 到一個 vector容器

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章