容器的概念所謂STL容器,即是將最常運用的一些數據結構(data structures)實現出來。容器是指容納特定類型對象的集合。根據數據在容器中排列的特性,容器可概分為序列式(sequence)和關聯式(associative)兩種。迭代器是一種檢查容器內元素并遍歷元素的數據類型。它提供類似指針的功能,對容器的內容進行走訪。#include例如:std::vectorIntVector;std::vector::iterator first = IntVector.begin();// begin()得到指向vector開頭的Iterator,*first得到開頭一個元素的值std::vector::iterator last = IntVector.end();// end()得到指向vector結尾的Iterator,*last得到最后一個元素的值序列式容器所謂序列式容器,其中的元素都可序(ordered),但未必有序(sorted)。數組為C++語言內置的序列容器,STL另外提供vector、list、deque(double - ended queue)。它們的差別在于訪問元素的方式,以及添加或刪除元素相關操作的運行代價。標準庫還提供了三種容器適配器(adapter),所謂適配器是根據原始的容器類型所提供的操作,通過定義新的操作接口,來適應基礎的容器類型。順序容器適配器包括stack、queue、priority_queue等序列式容器。其中stack和queue由于只是將deque改頭換面而成,技術上被歸類為一種配接器(adapter),priority_queue是有優先級管理的隊列。一.Vector1.vector的基本概念vector是標準C++建議替代C數組的動態數組模型,它維護的是一個連續線性空間。vector所采用的數據結構非常簡單:線性連續空間。它以兩個迭代器start和finish分別指向配置得到的連續空間中目前已被使用的范圍,并以迭代器end_of_storage指向整塊連續空間(含備用空間)的尾端。vector的實現技術,關鍵在于其對大小的控制以及重新分配時的數據移動效率。一旦vector原有空間用完,如果客戶端每新增一個元素,vector內部就只擴充一個元素的空間,實為不智。因為所謂擴充控件(不論多大),是“配置新空間(malloc) / 拷貝移動數據(memcpy) / 釋放舊空間(free)”的大工程,時間成本很高,應該采用某種未雨綢繆的空間配置策略。注意,所謂動態增加大小,并不是在原空間之后接續新空間(因為無法保證之后尚有可供配置的空間),而是每次再分配原大小兩倍的內存空間。因此,對vector的任何操作,一旦引起控件重新配置,指向原vector的所有迭代器就都失效了。由于vector維護的是一個連續線性空間,因此vector迭代器具備普通指針的功能,支持隨機存取,即vector提供的是Random Access Iterators。2.向量類模板std::vector的成員函數#includestd::vectorvec;std::vectorvec(size);std::vectorvec(size, value);std::vectorvec(myvector);std::vectorvec(first, last);Operators: == 、 != 、 <= 、 >= 、<、>、[]assign(first, last):用迭代器first, last所指定的元素取代向量元素assign(num, val):用val的num份副本取代向量元素at(n):等價于[]運算符,返回向量中位置n的元素,因其有越界檢查,故比[]索引訪問安全front():返回向量中第一個元素的引用back():返回向量中最后一個元素的引用begin():返回向量中第一個元素的迭代器end():返回向量中最后一個元素的下一個迭代器(僅作結束游標,不可解引用)max_size():返回向量類型的最大容量(2 ^ 30 - 1 = 0x3FFFFFFF)capacity():返回向量當前開辟的空間大小( <= max_size,與向量的動態內存分配策略相關)size():返回向量中現有元素的個數( <= capacity)clear():刪除向量中所有元素empty():如果向量為空,返回真erase(start, end):刪除迭代器start end所指定范圍內的元素erase(i):刪除迭代器i所指向的元素erase()返回指向刪除的最后一個元素的下一位置的迭代器insert(i, x);把x插入到迭代器i所指定的位置之前insert(i, n, x):把x的n份副本插入到迭代器i所指定的位置之前insert(i, start, end):把迭代器start和end所指定的范圍內的值插入到迭代器i所指定的位置之前push_back(x):把x推入(插入)到向量的尾部pop_back():彈出(刪除)向量最后一個元素rbegin():返回一個反向迭代器,該迭代器指向的元素越過了向量中的最后一個元素rend():返回一個反向迭代器,該迭代器指向向量中第一個元素reverse():反轉元素順序resize(n, x):把向量的大小改為n, 新元素的初值賦為xswap(vectorref):交換2個向量的內容3.動態字符串類std::stringstring是標準C++建議替代C字符串(以零結束的字符數組)的動態字符串模型,可以簡單的看做vector。#includestd::string str1;std::string str3(str2);std::string str2("this is a string");以下未列出與vector相同的通用操作。Operators: + 、 +=length():和size()函數功能相同data():取得字符串指針c_str():取得C風格字符串指針c_str()的流程是先調用terminate(),然后再返回data()。因此如果你對效率要求比較高,而且你的處理又不一定需要以 / 0的方式結束,最好選擇data()。但是對于一般的C函數中,需要以const char*為輸入參數,要使用c_str()函數。operator=:賦值操作符append():追加字符串replace():替換字符copy():拷貝自己的num個字符到str中(從索引index開始)。find():在字符串中查找指定字符, 返回基于0的索引號rfind():反向查找find_first_of():查找包含子串中的任何字符,返回第一個位置find_first_not_of():查找不包含子串中的任何字符,返回第一個位置find_last_of():查找包含子串中的任何字符,返回最后一個位置find_last_not_of():查找不包含子串中的任何字符,返回最后一個位置substr(n1, len):得到字符串從n1開始的長度為len的子串比較字符串(支持所有的關系運算符)compare 比較字符串operator+? 字符串銜接operator+= 增加操作符operator== 判斷是否相等operator!= 判斷是否不等于operator< 判斷是否小于operator>> 從輸入流中讀入字符串operator<< 字符串寫入輸出流getline 從輸入流中讀入一行二.list1.list的基本概念相對于vector的連續線性空間,list就顯得復雜許多,與向量(vector)相比, 它允許快速的插入和刪除,且每次插入或刪除一個元素,就配置或釋放一個元素空間。因此,list對于空間的運用絕對的精準,一點也不浪費。而且,對于任何位置的元素插入或元素移除,list永遠是常數時間。list不再能夠像vector那樣以普通指針作為迭代器,因為其節點不保證在儲存空間中連續存在。list迭代器必須有能力指向list的節點,并有能力進行正確的遞增、遞減、取值、成員存取等操作。所謂“list迭代器正確的遞增、遞減、取值、成員取用”操作是指,遞增時指向下一個節點,遞減時指向上一個節點,取值時取的是節點的數據值,成員取用時取用的是節點的成員。list不僅是一個雙向鏈表,而其還是一個環狀雙向鏈表。所以它只需要一個指針,便可以完整實現整個鏈表。由于list是一個雙向鏈表(double linked - list),迭代器必須具備前移、后移的能力,所以list提供的是Bidirectional Iterators。list有一個重要性質:插入操作(insert)和合并操作(splice)都不會造成原有的list迭代器失效。這在vector是不成立的,因為vector的插入操作可能造成記憶體重新配置,導致原有的迭代器全部失效。甚至list的元素刪除操作(erase)也只有“指向被刪除元素”的那個迭代器失效,其他迭代器不受任何影響。2.鏈表類模板std::list成員函數#includestd::listlst;std::listlst(size);std::listlst(size, value);std::listlst(mylist);std::listlst(first, last);以下未列出與vector相同的通用操作。push_front(x):把元素x推入(插入)到鏈表頭部pop_front():彈出(刪除)鏈表首元素merge(listref):把listref所引用的鏈表中的所有元素插入到鏈表中,可指定合并規則splice():把lst連接到pos的位置remove(val):刪除鏈表中所有值為val的元素remove_if(pred):刪除鏈表中謂詞pred為真的元素(謂詞即為元素存儲和檢索的描述,如std::less<>,std::greater<>那么就按降序 / 升序排列,你也可以定義自己的謂詞)sort():根據默認的謂詞對鏈表排序sort(pred):根據給定的謂詞對鏈表排序unique():刪除鏈表中所有重復的元素unique(pred):根據謂詞pred刪除所有重復的元素,使鏈表中沒有重復元素注意:vector和deque支持隨機訪問,而list不支持隨機訪問,因此不支持[]訪問!三.deque1.deque的基本概念vector是單向開口的連續線性空間,deque則是以中雙向開口的連續線性空間。所謂雙向開口,意思是可以在頭尾兩端分別做元素的插入和刪除操作。從技術的角度而言,vector當然也可以在頭尾兩端進行操作,但是其頭部操作效率奇差、令人無法接受。deque和vector的最大差異,一在于deque允許于常數時間內對頭端進行元素的插入或移除操作,二在于deque沒有所謂容量(capacity)觀念,因為它是動態地以分段連續空間組合而成,隨時可以增加一段新的空間并鏈接起來。換句話說,像vector那樣“因舊空間不足而重新配置一塊更大空間,然后復制元素,再釋放舊空間”這樣的事情在deque中是不會發生的。也因此,deque沒有必要提供所謂的空間預留(reserved)功能。雖然deque也提供Random Access Iterator,但它的迭代器并不是普通指針,其復雜度和vector不可同日而語,這當然涉及到各個運算層面。因此,除非必要,我們應盡可能選擇使用vector而非deque。對deque進行的排序操作,為了最高效率,可將deque先完整復制到一個vector身上,將vector排序后(利用STL的sort算法),再復制回deque。deque是由一段一段的定量連續空間構成。一旦有必要在deque的前端或尾端增加新空間,便配置一段定量的連續空間,串接在整個deque的頭端或尾端。deque的最大任務,便是在這些分段的定量連續空間上,維護其整體連續的假象,并提供隨機存取的接口。避開了“重新配置、復制、釋放”的輪回,代價則是復雜的迭代器架構。2.雙端隊列類模板std::deque成員函數#includestd::dequedeq;std::dequedeq(size);std::dequedeq(size, value);std::dequedeq(mydeque);std::dequedeq(first, last);其成員函數如下:Operators:[]用來訪問雙向隊列中單個的元素front():返回第一個元素的引用push_front(x):把元素x推入(插入)到雙向隊列的頭部pop_front():彈出(刪除)雙向隊列的第一個元素back():返回最后一個元素的引用push_back(x):把元素x推入(插入)到雙向隊列的尾部pop_back():彈出(刪除)雙向隊列的最后一個元素四.基于deque的順序容器適配器stack、queue(priority_queue)stack1.stack的基本概念stack是一種后進先出(First In Last Out,FILO)的數據結構,它只有一個出口。stack允許新增元素、移除元素、取得最頂端元素。但除了最頂端外,沒有任何其他方法可以存取stack的其他元素,換言之,stack不允許隨機訪問。STL以deque作為stack的底層結構,對deque封閉期頭端開口,稍作修改便形成了stack。將元素插入stack的操作稱為push,將元素彈出stack的操作稱為pop。stack所有元素的進出都必須符合“后進先出”的條件,只有stack頂端的元素,才有機會被外界取用。stack不提供走訪功能,也不提供迭代器。2.容器適配器堆棧類std::stack成員函數#includestack實現后進先出的操作std::stackstk;type為堆棧操作的數據類型container為實現堆棧所用的容器類型,默認基于deque,還可以為std::vector和std::list例如std::stack> IntStack;其成員函數如下:top():返回頂端元素的引用push(x):將元素壓入棧(頂)pop():彈出(刪除)頂端元素queue1.queue的基本概念queue是一種先進先出(First In First Out,FIFO)的數據結構,它有兩個出口。queue允許新增元素、移除元素、從最底端加入元素、取得最頂端元素。但除了最底端可以加入、最頂端可以取出,沒有任何其他方法可以存取queue的其他元素。換言之,queue不支持隨機訪問。STL以deque作為queue的底層結構,對deque封閉其底端的出口和前端的入口,稍作修改便形成了queue。2.容器適配器隊列類std::queue成員函數#includequeue實現先進先出的操作std::queueque;type為隊列操作的數據類型container為實現隊列所用的容器類型,只能為提供了push_front操作的std::deque或std::list,默認基于std::deque其成員函數如下:front():返回隊首元素的引用back():返回隊尾元素的引用push(x):把元素x推入(插入)到隊尾pop():隊首元素出列(彈出(刪除)隊首元素)priority_queue1.priority_queue的基本概念priority_queue為優先級隊列,它允許用戶為隊列中存儲的元素設置優先級。這種隊列不是直接將新元素放置在隊列尾部,而是放置在比它優先級低的元素前面,即提供了一種插隊策略。標準庫默認使用<操作符來確定他們之間的優先級關系。即權重大的排在隊首。使用priority_queue時,包含文件。2.容器適配器隊列類std::priority_queue成員函數#includepriority_queue實現先進先出的操作std::priority_queuepri_que;type為隊列操作的數據類型container為實現隊列所用的容器類型,可以為std::vector, std::deque,默認基于dequecomp為排隊策略,默認為std::less<>,即插到小于它的元素前例如std::priority_queue, std::greater> IntPriQue;其成員函數如下:top():返回隊首(優先級最高)元素的引用push(x):將元素推入(按插隊策略插排)隊列(尾部)pop():彈出(刪除)隊首(優先級最高)元素關聯式容器所謂關聯式容器,概念上類似關聯式數據庫(實際上則簡單許多):每項數據(元素)包含一個鍵值(key)和一個實值(value)。當元素被插入到關聯式容器中時,容器內部數據結構(可能是RB - tree,也可能是hash - table)便依照其鍵值大小,以某種特定規則將這個元素放置于適當位置。關聯式容器沒有所謂頭尾(只有最大元素和最小元素),所以不會有push_back(),push_front(),pop_back(),pop_front(),begin(),end()這樣的操作。一般而言,關聯式容器的內部結構是一個balanced binary tree(平衡二叉樹),以便獲得良好的搜索效率。balanced binary tree有很多種類型,包括AVL - tree、RB - tree、AA - tree,其中廣泛運用于STL的是RB - tree(紅黑樹)。標準的STL關聯式容器分為set(集合)和map(映射類)兩大類,以及這兩大類的衍生體multiset(多鍵集合)和multimap(多鍵映射表)。這些容器的底層機制均以RB - tree完成(紅黑樹)。RB - tree也是一個獨立容器,但并不開放給外界使用。此外,SGI STL還提供了一個不在標準規格之列的關聯式容器:hash table(散列表,哈希表),以及以此hash table為底層機制而完成的hash_set(散列集合)、hash_map(散列映射表)、hash_multiset(散列多鍵集合)、hash_multimap(散列多鍵映射表)。map關聯式容器std::map成員函數#includemap建立key - value映射std::mapmp;std::mapmp;
key為鍵值
value為映射值
comp可選,為鍵值對存放策略,例如可為std::less<>,鍵值映射對將按鍵值從小到大存儲
其成員函數如下:
count():返回map中鍵值等于key的元素的個數
equal_range():函數返回兩個迭代器——一個指向第一個鍵值為key的元素,另一個指向最后一個鍵值為key的元素
erase(i):刪除迭代器所指位置的元素(鍵值對)
lower_bound():返回一個迭代器,指向map中鍵值 >= key的第一個元素
upper_bound():函數返回一個迭代器,指向map中鍵值>key的第一個元素
find(key):返回鍵值為key的鍵值對迭代器,如果沒有該映射則返回結束游標end()
注意map的[]操作符,當試圖對于不存在的key進行引用時,將新建鍵值對,值為空。