1. 程式人生 > >STL容器與資料結構的對應關係

STL容器與資料結構的對應關係

STL中的常用容器包括:順序性容器(vector、deque、list)、關聯容器(map、set)、容器介面卡(queue、stac)。

2、關聯容器

(1)map map是一種關聯容器,該容器用唯一的關鍵字來對映相應的值,即具有key-value功能。map內部自建一棵紅黑樹(一種自平衡二叉樹),這棵樹具有資料自動排序的功能,所以在map內部所有的資料都是有序的,以二叉樹的形式進行組織。這是map的模板: template < class Key, class T, class Compare= less<Key>, class Allocator=allocator< pair<const Key,T> > > class map; 從模板中我們可以看出,再構造map時,是按照一定的順序進行的。map的插入和刪除效率比其他序列的容器高,因為對關聯容器來說,不需要做記憶體的拷貝和移動,只是指標的移動。由於map的每個資料對應紅黑樹上的一個節點,這個節點在不儲存你的資料時,是佔用16個位元組的,一個父節點指標,左右孩子指標,還有一個列舉值(標示紅黑色),所以map的其中的一個缺點就是比較佔用記憶體空間。

map簡介

 map是STL的一個關聯容器(associative container)之一,它提供一對一(其中第一個可以稱為關鍵字,每個關鍵字只能在map中出現一次,第二個可能稱為該關鍵字的值)的資料處理能力,由於這個特性,它完成有可能在我們處理一對一資料的時候,在程式設計上提供快速通道。這裡說下map內部資料的組織,map內部自建一顆紅黑樹(一種非嚴格意義上的平衡二叉樹),這顆樹具有對資料自動排序的功能,所以在map內部所有的資料都是有序的。 

一個map是一個鍵值對序列,

         即(key ,value)對。它提供基於key的快速檢索能力,在一個map中key值是唯一的。map提供雙向迭代器,即有從前往後的(iterator),也有從後往前的(reverse_iterator)。

map中key值是唯一 

    例子:如果已存在一個鍵值對(編號,使用者名稱):(1001,"jack"),而我們還想插入一個鍵值對(1001,"mike") 執行時會報錯(不是報錯,準確的說是,返回插入不成功!)。而我們又的確想這樣做,即一個鍵對應多個值,幸運的是multimap可是實現這個功能。

(2)set set也是一種關聯性容器,它同map一樣,底層使用紅黑樹實現,插入刪除操作時僅僅移動指標即可,不涉及記憶體的移動和拷貝,所以效率比較高。set中的元素都是唯一的,而且預設情況下會對元素進行升序排列。所以在set中,不能直接改變元素值,因為那樣會打亂原本正確的順序,要改變元素值必須先刪除舊元素,再插入新元素。不提供直接存取元素的任何操作函式,只能通過迭代器進行間接存取。set模板原型: template <class Key, class Compare=class<Key>, class Alloc=STL_DEFAULT_ALLOCATOR(Key) > class set; set支援集合的交(set_intersection)、差(set_difference)、並(set_union)及對稱差(set_symmetric_difference) 等一些集合上的操作。

3、容器介面卡

(1)queue queue是一個佇列,實現先進先出功能,queue不是標準的STL容器,卻以標準的STL容器為基礎。queue是在deque的基礎上封裝的。之所以選擇deque而不選擇vector是因為deque在刪除元素的時候釋放空間,同時在重新申請空間的時候無需拷貝所有元素。其模板為: template < TYPENAME _Sequence="deque<_TP" typeneam _Tp,> > class queue;

(2)stack stack是實現先進後出的功能,和queue一樣,也是內部封裝了deque,這也是為啥稱為容器介面卡的原因吧(純屬猜測)。自己不直接維護被控序列的模板類,而是它儲存的容器物件來為它實現所有的功能。stack的原始碼原理和實現方式均跟queue相同。

1、順序性容器

(1)vector vector是一種動態陣列,在記憶體中具有連續的儲存空間,支援快速隨機訪問。由於具有連續的儲存空間,所以在插入和刪除操作方面,效率比較慢。vector有多個建構函式,預設的建構函式是構造一個初始長度為0的記憶體空間,且分配的記憶體空間是以2的倍數動態增長的,即記憶體空間增長是按照20,21,22,23.....增長的,在push_back的過程中,若發現分配的記憶體空間不足,則重新分配一段連續的記憶體空間,其大小是現在連續空間的2倍,再將原先空間中的元素複製到新的空間中,效能消耗比較大,尤其是當元素是非內部資料時(非內部資料往往構造及拷貝建構函式相當複雜)。vector的另一個常見的問題就是clear操作。clear函式只是把vector的size清為零,但vector中的元素在記憶體中並沒有消除,所以在使用vector的過程中會發現記憶體消耗會越來越多,導致記憶體洩露,現在經常用的方法是swap函式來進行解決:   vector<int> V;V.push_back(1); V.push_back(2);V.push_back(1); V.push_back(2); vector<int>().swap(V); 或者 V.swap(vector<int>());

利用swap函式,和臨時物件交換,使V物件的記憶體為臨時物件的記憶體,而臨時物件的記憶體為V物件的記憶體。交換以後,臨時物件消失,釋放記憶體。 -------------------

STL: 知道vector嗎,一個自然數陣列,要刪除其中的奇數,寫出程式碼。這個需要注意的是erase刪除結點,則該結點的iterator會失效,同時erase會返回下一個有效迭代器,所以iter++只有在偶數的時候才執行。 繼續問到iter++和++iter的區別,回答前者會產生一個臨時變數,後者的效率更高,如果前面有=的話,得到的值不一樣。 vector是怎麼儲存的,如果讓你實現vector,你怎麼做,首先說了下STL裡面的vector,記憶體如何分配的,建構函式等等。開始用陣列實現vector,然後問一定要寫成類似MS提供的STL裡面的vector,提示說不用,寫個基本框架就可以了,就直接寫vector程式碼,然後寫vector的插入操作,注意vector滿時

-------------------(2)deque deque和vector類似,支援快速隨機訪問。二者最大的區別在於,vector只能在末端插入資料,而deque支援雙端插入資料。deque的記憶體空間分佈是小片的連續,小片間用連結串列相連,實際上內部有一個map的指標。deque空間的重新分配要比vector快,重新分配空間後,原有的元素是不需要拷貝的。

(3)list list是一個雙向連結串列,因此它的記憶體空間是可以不連續的,通過指標來進行資料的訪問,這使list的隨機儲存變得非常低效,因此list沒有提供[]操作符的過載。但list可以很好地支援任意地方的插入和刪除,只需移動相應的指標即可。

        我們常用到的STL容器有vector、list、deque、map、multimap、set和multiset,它們的區別和各自的優缺點如下:

verctor

vector類似於C語言中的陣列,它維護一段連續的記憶體空間,具有固定的起始地址,因而能非常方便地進行隨機存取,即 [] 操作符,但因為它的記憶體區域是連續的,所以在它中間插入或刪除某個元素,需要複製並移動現有的元素。此外,當被插入的記憶體空間不夠時,需要重新申請一塊足夠大的記憶體並進行記憶體拷貝。值得注意的是,vector每次擴容為原來的兩倍,對小物件來說執行效率高,但如果遇到大物件,執行效率就低了。

list

list類似於C語言中的雙向連結串列,它通過指標來進行資料的訪問,因此維護的記憶體空間可以不連續,這也非常有利於資料的隨機存取,因而它沒有提供 [] 操作符過載。

deque

deque類似於C語言中的雙向佇列,即兩端都可以插入或者刪除的佇列。queue支援 [] 操作符,也就是支援隨機存取,而且跟vector的效率相差無幾。它支援兩端的操作:push_back,push_front,pop_back,pop_front等,並且在兩端操作上與list的效率 也差不多。或者我們可以這麼認為,deque是vector跟list的折中。

map

map類似於資料庫中的1:1關係,它是一種關聯容器,提供一對一(C++ primer中文版中將第一個譯為鍵,每個鍵只能在map中出現一次,第二個被譯為該鍵對應的值)的資料處理能力,這種特性了使得map類似於資料結構裡的紅黑二叉樹。

multimap

multimap類似於資料庫中的1:N關係,它是一種關聯容器,提供一對多的資料處理能力。

set

set類似於數學裡面的集合,不過set的集合中不包含重複的元素,這是和vector的第一個區別,第二個區別是set內部用平衡二叉樹實現,便於元素查詢,而vector是使用連續記憶體儲存,便於隨機存取。

multiset

multiset類似於數學裡面的集合,集合中可以包含重複的元素。

小結

在實際使用過程中,到底選擇這幾種容器中的哪一個,應該根據遵循以下原則:

1、如果需要高效的隨機存取,不在乎插入和刪除的效率,使用vector;

2、如果需要大量的插入和刪除元素,不關心隨機存取的效率,使用list;

3、如果需要隨機存取,並且關心兩端資料的插入和刪除效率,使用deque;

4、如果打算儲存資料字典,並且要求方便地根據key找到value,一對一的情況使用map,一對多的情況使用multimap;

5、如果打算查詢一個元素是否存在於某集合中,唯一存在的情況使用set,不唯一存在的情況使用multiset。