STL詳解
STL概貌
STL 包含 5 個主要的部分
·演算法(Algorithm):能執行在不同容器(container)上的計算過程
·容器(Container):能夠保留並管理物件的物件
·迭代器(Iterator):演算法存取容器(algorithm-access to containers)的抽象,以便演算法可以應用在不同的容器上
·函式物件(Function Object):定義了函式呼叫操作符(operator())的類
·介面卡(Adaptor):封裝一個部件以提供另外的介面(例如用list實現stack
#include <vector> //include 你想用的 STL 標頭檔案 using namespace std; //一定要寫上這句話,因為 STL 都是在 std 名字空間中定義的
1 容 器
容器(Container)是能夠儲存其他型別的物件的類。容器形成了 STL 的關鍵部件。當書寫任何型別軟體的時候,把特定型別的元素集聚起來都是很至關重要的任務。
STL 中有順序容器(Sequence Container)和關聯容器(Associative Container)。
順序容器組織成物件的有限線性集合,所有物件都是同一型別。 STL 中三種基本順序容器是:向量(Vector)、線性表(List)、雙向佇列(Deque)。
關聯容器提供了基於 KEY 的資料的快速檢索能力。元素被排好序,檢索資料時可以二分搜尋。STL 有四種關聯容器。當一個 KEY 對應一個 Value 時,可以使用集合(Set)和對映(Map);若對應同一 KEY 有多個元素被儲存時,可以使用多集合(MultiSet)和多對映(MultiMap)
C++ STL (Standard Template Library標準模板庫) 是通用類模板和演算法的集合,它提供給程式設計師一些標準的資料結構的實現如 queues(佇列), lists(連結串列), 和stacks(棧)等.
C++ STL 提供給程式設計師以下三類資料結構的實現:
順序性容器
vector 從後面快速的插入與刪除,直接訪問任何元素
deque 從前面或後面快速的插入與刪除,直接訪問任何元素
list 雙鏈表,從任何地方快速插入與刪除
關聯容器
set 快速查詢,不允許重複值
multiset 快速查詢,允許重複值
map 一對一對映,基於關鍵字快速查詢,不允許重複值
multimap 一對多對映,基於關鍵字快速查詢,允許重複值
容器介面卡
stack 後進先出
queue 先進先出
priority_queue 最高優先順序元素總是第一個出列
程式設計師使用複雜資料結構的最困難的部分已經由STL完成. 如果程式設計師想使用包含int資料的stack, 他只要寫出如下的程式碼:
stack<int> myStack;
接下來, 他只要簡單的呼叫 push() 和 pop() 函式來操作棧. 藉助 C++ 模板的威力, 他可以指定任何的資料型別,不僅僅是int型別. STL stack實現了棧的功能,而不管容納的是什麼資料型別
1.1 順序性容器
順序性容器包括:
vector:從後面快速的插入與刪除,直接訪問任何元素。
deque: 從前面或後面快速的插入與刪除,直接訪問任何元素。
list: 雙鏈表,從任何地方快速插入與刪除。
1.1.1 vector
vector是一個線性順序結構。相當於陣列,但其大小可以不預先指定,並且自動擴充套件。它可以像陣列一樣被操作,由於它的特性我們完全可以將vector看作動態陣列。
在建立一個vector 後,它會自動在記憶體中分配一塊連續的記憶體空間進行資料儲存,初始的空間大小可以預先指定也可以由vector 預設指定,這個大小即capacity()函式的返回值。當儲存的資料超過分配的空間時vector 會重新分配一塊記憶體塊,但這樣的分配是很耗時的,
在重新分配空間時它會做這樣的動作:
1. vector 會申請一塊更大的記憶體塊;
2. 將原來的資料拷貝到新的記憶體塊中;
3. 銷燬掉原記憶體塊中的物件(呼叫物件的解構函式);
4. 將原來的記憶體空間釋放掉
如果vector 儲存的資料量很大時,這樣的操作一定會導致糟糕的效能( 這也是vector 被設計成比較容易拷貝的值型別的原因)。
所以說vector 不是在什麼情況下效能都好,只有在預先知道它大小的情況下vector 的效能才是最優的。
vector 的特點:
(1) 指定一塊如同陣列一樣的連續儲存,但空間可以動態擴充套件。即它可以像陣列一樣操作,並且可以進行動態操作。通常體現在push_back() pop_back() 。
(2) 隨機訪問方便,它像陣列一樣被訪問,即支援[ ] 操作符和vector.at()
(3) 節省空間,因為它是連續儲存,在儲存資料的區域都是沒有被浪費的,但是要明確一點vector 大多情況下並不是滿存的,在未儲存的區域實際是浪費的。
(4) 在內部進行插入、刪除效率非常低,這樣的操作基本上是被禁止的。Vector 被設計成只能在後端進行追加和刪除操作,其原因是vector 內部的實現是按照順序表的原理。
(5) 只能在vector 的最後進行push 和pop ,不能在vector 的頭進行push 和pop 。
(6) 當動態新增的資料超過vector 預設分配的大小時要進行記憶體的重新分配、拷貝與釋放,這個操作非常消耗效能。 所以要vector 達到最優的效能,最好在建立vector 時就指定其空間大小。
Vectors 包含著一系列連續儲存的元素,其行為和陣列類似。訪問Vector中的任意元素或從末尾新增元素都可以在常量級時間複雜度內完成,而查詢特定值的元素所處的位置或是在Vector中插入元素則是線性時間複雜度。
1.Constructors 建構函式 vector<int> v1; //構造一個空的vector vector<int> v1( 5, 42 ); //構造了一個包含5個值為42的元素的Vector 2.Operators 對vector進行賦值或比較 C++ Vectors能夠使用標準運算子: ==, !=, <=, >=, <, 和 >.要訪問vector中的某特定位置的元素可以使用 [] 操作符. 兩個vectors被認為是相等的,如果: 1.它們具有相同的容量 2.所有相同位置的元素相等. vectors之間大小的比較是按照詞典規則. 3.assign() 對Vector中的元素賦值 語法: void assign( input_iterator start, input_iterator end ); // 將區間[start, end)的元素賦到當前vector void assign( size_type num, const TYPE &val ); // 賦num個值為val的元素到vector中,這個函式將會清除掉為vector賦值以前的內容. 4.at() 返回指定位置的元素 語法: TYPE at( size_type loc );//差不多等同v[i];但比v[i]安全; 5.back() 返回最末一個元素 6.begin() 返回第一個元素的迭代器 7.capacity() 返回vector所能容納的元素數量(在不重新分配記憶體的情況下) 8.clear() 清空所有元素 9.empty() 判斷Vector是否為空(返回true時為空) 10.end() 返回最末元素的迭代器(譯註:實指向最末元素的下一個位置) 11.erase() 刪除指定元素 語法: iterator erase( iterator loc );//刪除loc處的元素 iterator erase( iterator start, iterator end );//刪除start和end之間的元素 12.front() 返回第一個元素的引用 13.get_allocator() 返回vector的記憶體分配器 14.insert() 插入元素到Vector中 語法: iterator insert( iterator loc, const TYPE &val ); //在指定位置loc前插入值為val的元素,返回指向這個元素的迭代器, void insert( iterator loc, size_type num, const TYPE &val ); //在指定位置loc前插入num個值為val的元素 void insert( iterator loc, input_iterator start, input_iterator end ); //在指定位置loc前插入區間[start, end)的所有元素 15.max_size() 返回Vector所能容納元素的最大數量(上限) 16.pop_back() 移除最後一個元素 17.push_back() 在Vector最後新增一個元素 18.rbegin() 返回Vector尾部的逆迭代器 19.rend() 返回Vector起始的逆迭代器 20.reserve() 設定Vector最小的元素容納數量 //為當前vector預留至少共容納size個元素的空間 21.resize() 改變Vector元素數量的大小 語法: void resize( size_type size, TYPE val ); //改變當前vector的大小為size,且對新建立的元素賦值val 22.size() 返回Vector元素數量的大小 23.swap() 交換兩個Vector 語法: void swap( vector &from );vector常用函式
如果你要將所有 shape 物件儲存在一個容器中,用 C++程式碼可以這樣寫:
Shape my_shapes[max_size];
這裡 max_size 是可以儲存在 my_shapes 陣列中的最大數量。當你使用 STL 時,則可以這樣寫:
#include <vector> using namespace std; int main() { vector<shape> my_shapes; // … 使用 my_shapes… return 0; }
現在想得到容器中能儲存的最大元素數量就可以用 vector 類的成員函式 max_size():
vector<shape>::size_type max_size = my_shapes.max_size();
當前容器的實際尺寸 --- 已有的元素個數用 size():
vector<shape>::size_type size = my_shapes.size();
就像 size_type 描述了 vector 尺寸的型別,value_type 說明了其中儲存的物件的型別:
cout << “value type: “ << typeid(vector<float>::value_type).name();
輸出:
value type: float
可以用 capacity()來取得 vector 中已分配記憶體的元素個數:
vector<int> v;
vector<int>::size_type capacity = v.capacity();
vector 類似於陣列,可以使用下標[]訪問:
vector<int> v(10);
v[0] = 101;
注意到這裡預先給 10 個元素分配了空間。你也可以使用 vector 提供的插入函式來動態的擴充套件容器。成員函式 push_back()就在 vector 的尾部添加了一個元素:
v.push_back(3);
也可以用 insert()函式完成同樣的工作:
v.insert(v.end(), 3);
這裡 insert()成員函式需要兩個引數:一個指向容器中指定位置的迭代器(iterator),一個待插入的元素。insert()將元素插入到迭代器指定元素之前。
現在對迭代器(Iterator)做點解釋。Iterator 是指標(pointer)的泛化,iterator 要求定義operator*,它返回指定型別的值。Iterator 常常和容器聯絡在一起。例子:
vector<int> v(3); v[0] = 5; v[1] = 2; v[2] = 7;11 vector<int>::iterator first = v.begin(); vector<int>::iterator last = v.end(); while (first != last) cout << *first++ << “ “; 輸出: 5 2 7
begin()返回的是 vector 中第一個元素的 iterator,而 end()返回的並不是最後一個元素的iterator,而是 past the last element。
在 STL 中叫 past-the-end iterator。即 越尾指標
vector<float> v(5, 3.25); //初始化有 5 個元素,其值都是 3.25 vector<float> v_new1(v); vector<float> v_new2 = v; vector<float> v_new3(v.begin(), v.end()); //這四個 vector 物件是相等的,可以用 operator==來判斷
例項:
#include <iostream> #include <string> #include <vector> using namespace std; #if 0 /* Vector容器簡介 vector是將元素置於一個動態陣列中加以管理的容器。 vector可以隨機存取元素(支援索引值直接存取, 用[]操作符或at()方法)。 vector尾部新增或移除元素非常快速。但是在中部或頭部插入元素或移除元素比較費時 標頭檔案:#include<vector> vector物件的預設構造 vector採用模板類實現,vector物件的預設構造形式: vector<T> vecT; 容器中可以新增常規資料型別: vector<int> vecInt; //一個存放int的vector容器。 vector<float> vecFloat; //一個存放float的vector容器。 vector<string> vecString;//一個存放string的vector容器。 尖括號內還可以設定指標型別或自定義型別: Class CA{}; vector<CA*> vecpCA; //用於存放CA物件的指標的vector容器。 vector<CA> vecCA; 用於存放CA物件的vector容器。由於容器元素的存放是按值複製的方式進行的, 所以此時CA必須提供CA的拷貝建構函式,以保證CA物件間拷貝正常。 在末尾新增元素: push_back(element) (push_back兼有開闢空間和新增資料的功能,只有開闢完空間才能使用[]訪問元素) 在末尾移除元素: pop_back() vector的資料存取 vec.at(idx); 返回索引idx所指的資料,如果idx越界,丟擲out_of_range異常。 vec[idx]; 返回索引idx所指的資料,越界時,執行直接報錯 vec.front(); 返回第一個元素的“引用” vec.back(); 返回最後一個元素的“引用” vector<int> vecInt; //假設包含1 ,3 ,5 ,7 ,9 vecInt.at(2) == vecInt[2] ; //5 vecInt.at(2) = 8; 或 vecInt[2] = 8; vecInt 就包含 1, 3, 8, 7, 9值 int iF = vector.front(); //iF==1 int iB = vector.back(); //iB==9 vector.front() = 11; //vecInt包含{11,3,8,7,9} vector.back() = 19; //vecInt包含{11,3,8,7,19} vector的大小 vec.size() 返回容器中元素的個數 vec.empty() 判斷容器是否為空 vec.resize(num) 重新指定容器的長度為num,若容器變長,則以預設值填充新 位置。如果容器變短,則末尾超出容器長度的元素被刪除。 vec.resize(num, elem) 重新指定容器的長度為num,若容器變長,則以elem值 填充新位置。如果容器變短,則末尾超出容器長度的元素被刪除。 例如 vecInt是vector<int> 宣告的容器,現已包含1,2,3元素。 int iSize = vecInt.size(); //iSize == 3; bool bEmpty = vecInt.empty(); // bEmpty == false; 執行vecInt.resize(5); //此時裡面包含1,2,3,0,0元素。 再執行vecInt.resize(8,3); //此時裡面包含1,2,3,0,0,3,3,3元素。 再執行vecInt.resize(2); //此時裡面包含1,2元素。 vector物件的帶引數構造 vector v(beg,end) 建構函式將[beg, end)區間中的元素拷貝給本身。注意該區間是左閉右開的區間。 vector v(n) 建立有n個初始化元素的容器v vector v(n,elem) 建構函式將n個elem拷貝給本身。 vector v(const vector &vec) 拷貝建構函式 */ #endif //使用陣列的方式列印 void print_vector(vector<string> &vec) { cout<<"---------use Array-----------"<<endl; //char * //string --> char * data() c_str() for(int i=0;i<vec.size();i++) cout<<vec[i].data()<<" "; cout<<endl; cout<<"---------use Array-----------"<<endl; } //使用迭代器遍歷 void pintIt_vector(vector<string> &vec) { cout<<"---------use iterator-----------"<<endl; vector<string>::iterator it=vec.begin(); for(;it!=vec.end();++it) cout<<*it<<" "; cout<<endl; cout<<"---------use iterator-----------"<<endl; } //逆向遍歷 void print_vector_reverse(vector<string> &vec) { cout<<"---------use reverse_iterator-----------"<<endl; vector<string>::reverse_iterator it=vec.rbegin(); for(;it!=vec.rend();++it) cout<<*it<<" "; cout<<endl; cout<<"---------use reverse_iterator-----------"<<endl; } int main() { vector<int> vInt; //在末尾新增元素,同時開闢空間 vInt.push_back(1); vInt.push_back(2); //在末尾移除元素,同時刪除空間 vInt.pop_back(); //得到vInt的大小。 cout<<vInt.size()<<endl; vInt.pop_back(); cout<<vInt.size()<<endl; if (vInt.empty()) cout<<"vInt is empty"<<endl; vInt.resize(5); for(int i=0;i<5;i++) cout<<vInt[i]<<" "; cout<<endl; vInt.resize(8,3); for(int i=0;i<8;i++) cout<<vInt[i]<<" "; cout<<endl; vInt.front()=10; vInt.resize(2); cout<<vInt[0]<<" "<<vInt[1]<<endl; for(int i=0;i<10;i++)vInt[i]=i; for(int i=0;i<10;i++)cout<<vInt[i]<<" "; cout<<endl; cout<<vInt.size()<<endl; //因為沒有push_back,所以沒有新開闢空間 for(vector<int>::iterator it=vInt.begin();it!=vInt.end();++it)cout<<*it<<" "; cout<<endl; //建立空物件 vector<string> vec; if (vec.empty()) { cout << "an empty vector" << endl; } //在末尾新增元素 vec.push_back("one"); vec.push_back("two"); vec.push_back("three"); vec.push_back("four"); //列印vector的長度 cout << "vector size = " << vec.size() << endl; cout << "vectro front element = " << vec.front().data()<<endl; cout << "vectro front element = " << vec.front().c_str()<<endl; cout << "modify front element = huangdi" << endl; vec.front() = "huangdi"; cout<<"after modify front element is :"<<vec.front().data()<<endl; print_vector(vec); pintIt_vector(vec); print_vector_reverse(vec); cout<<"vector back element: "<<vec.back().data()<<endl; cout<<"modify back element = qinshihuang"<<endl; vec.back()="qinshihuang"; pintIt_vector(vec); //刪除vector中的資料 //方案1: 使用pop_back依次從後面向前刪除 //while (vec.size() > 0) //{ // vec.pop_back(); //} //cout << "vector size = " << vec.size() << endl; //方案2: 重置空間大小為0 vec.resize(0); cout << "vector size = " << vec.size() << endl; //方案3:使用erase 函式 return 0; }View Code
1.1.2 List
C++ List( 雙向連結串列)是一個線性連結串列結構,它的資料由若干個節點構成,每一個節點都包括一個資訊塊(即實際儲存的資料)、一個前驅指標和一個後驅指標。它無需分配指定的記憶體大小且可以任意伸縮,這是因為它儲存在非連續的記憶體空間中,並且由指標將有序的元素連結起來。由於其結構的原因, list 隨機檢索的效能非常的不好,因為它不像vector 那樣直接找到元素的地址,而是要從頭一個一個的順序查詢,這樣目標元素越靠後,它的檢索時間就越長。檢索時間與目標元素的位置成正比。雖然隨機檢索的速度不夠快,但是它可以迅速地在任何節點進行插入和刪除操作。因為list 的每個節點儲存著它在連結串列中的位置,插入或刪除一個元素僅對最多三個元素有所影響,不像vector 會對操作點之後的所有元素的儲存地址都有所影響,這一點是vector 不可比擬的。
list 的特點:
(1) 不使用連續的記憶體空間這樣可以隨意地進行動態操作;
(2) 可以在內部任何位置快速地插入或刪除,當然也可以在兩端進行push和pop 。
(3) 不能進行內部的隨機訪問,即不支援[ ] 操作符和vector.at() ;
Lists將元素按順序儲存在連結串列中, 與向量(vectors)相比, 它允許快速的插入和刪除,但是隨機訪問卻比較慢.
list函式方法:
1.assign() 給list賦值 語法: void assign( input_iterator start, input_iterator end ); //以迭代器start和end指示的範圍為list賦值 void assign( size_type num, const TYPE &val ); //賦值num個以val為值的元素。 2.back() 返回最後一個元素的引用 3.begin() 返回指向第一個元素的迭代器 4.clear() 刪除所有元素 5.empty() 如果list是空的則返回true 6.end() 返回末尾的迭代器 7.erase() 刪除一個元素 語法: iterator erase( iterator loc );//刪除loc處的元素 iterator erase( iterator start, iterator end ); //刪除start和end之間的元素 8.front() 返回第一個元素的引用 9.get_allocator() 返回list的配置器 10.insert() 插入一個元素到list中 語法: iterator insert( iterator loc, const TYPE &val ); //在指定位置loc前插入值為val的元素,返回指向這個元素的迭代器, void insert( iterator loc, size_type num, const TYPE &val ); //定位置loc前插入num個值為val的元素 void insert( iterator loc, input_iterator start, input_iterator end ); //在指定位置loc前插入區間[start, end)的所有元素 11.max_size() 返回list能容納的最大元素數量 12.merge() 合併兩個list 語法: void merge( list &lst );//把自己和lst連結串列連線在一起 void merge( list &lst, Comp compfunction ); //指定compfunction,則將指定函式作為比較的依據。 13.pop_back() 刪除最後一個元素 14.pop_front() 刪除第一個元素 15.push_back() 在list的末尾新增一個元素 16.push_front() 在list的頭部新增一個元素 17.rbegin() 返回指向第一個元素的逆向迭代器 18.remove() 從list刪除元素 語法: void remove( const TYPE &val ); //刪除連結串列中所有值為val的元素 19.remove_if() 按指定條件刪除元素 20.rend() 指向list末尾的逆向迭代器 21.resize() 改變list的大小 語法: void resize( size_type num, TYPE val ); //把list的大小改變到num。被加入的多餘的元素都被賦值為val22. 22.reverse() 把list的元素倒轉 23.size() 返回list中的元素個數 24.sort() 給list排序 語法: void sort();//為連結串列排序,預設是升序 void sort( Comp compfunction );//採用指定函式compfunction來判定兩個元素的大小。 25.splice() 合併兩個list 語法: void splice( iterator pos, list &lst );//把lst連線到pos的位置 void splice( iterator pos, list &lst, iterator del );//插入lst中del所指元素到現連結串列的pos上 void splice( iterator pos, list &lst, iterator start, iterator end );//用start和end指定範圍。 26.swap() 交換兩個list 語法: void swap( list &lst );// 交換lst和現連結串列中的元素 27.unique() 刪除list中重複的元素 語法: void unique();//刪除連結串列中所有重複的元素 void unique( BinPred pr );// 指定pr,則使用pr來判定是否刪除。list函式
例項:
#include <iostream> #include <list> using namespace std; //遍歷list void list_print(list<int> &ls) { cout<<"start echo: "; for(list<int>::iterator it=ls.begin();it!=ls.end();it++) cout<<*it<<" "; cout<<endl; } //在末尾新增元素(尾插法) void list_append(list<int> &ls,int beg,int end) { for(int i=beg;i<=end;i++) ls.push_back(i); } //在頭部新增元素(頭插法) void list_append_h(list<int> &ls,int beg,int end) { for(int i=beg;i<=end;i++) ls.push_front(i); } int main() { //預設構造 list<int> ls; list_append_h(ls,1,10); cout<<"echo list: "; list_print(ls); //尾部彈出一個元素 ls.pop_back(); cout<<"list size :"<<ls.size()<<endl; list_print(ls); //頭部新增元素 list_append_h(ls,20,25); list_print(ls); ls.pop_front(); list_print(ls); //修改頭部和尾部的資料 ls.front()=100; ls.back()=200; list_print(ls); cout<<"list all element: "; list_print(ls); //使用區間構造list物件 list<int> ls_1(++ls.begin(),--ls.end()); cout<<"ls_1 "; list_print(ls_1); //使用一個物件構造物件 list<int> ls_2(ls); cout<<"ls_2 "; list_print(ls_2); //賦值,預設建立的list大小是0,賦值時會自動增加大小 list<int> ls_3; ls_3.assign(10,9); cout<<"ls_3 "; list_print(ls_3); cout<<"-------swap---------"<<endl; ls_3.swap(ls); list_print(ls); list_print(ls_3); //插入 cout<<"insert before ls is :"; list_print(ls); ls.insert(ls.begin(),ls_3.begin(),ls_3.end()); cout<<"insert after ls is :"; list_print(ls); //刪除 ls.remove(9); //把所有的9全部移除 list_print(ls); list<int>::iterator it=ls.begin(); while(it!=ls.end()) { if(*it==5) { it=ls.erase(it); } else { it++; } } list_print(ls); //反轉list ls.reverse(); list_print(ls); //list排序(list只能使用自身的sort函式進行排序) ls.sort(); list_print(ls); return 0; }View Code
1.1.3 Deque
C++ Deque(雙向佇列)是一種優化了的、對序列兩端元素進行新增和刪除操作的基本序列容器。它允許較為快速地隨機訪問,但它不像vector 把所有的物件儲存在一塊連續的記憶體塊,而是採用多個連續的儲存塊,並且在一個對映結構中儲存對這些塊及其順序的跟蹤。向deque 兩端新增或刪除元素的開銷很小。它不需要重新分配空間,所以向末端增加元素比vector 更有效。實際上, deque 是對vector 和list 優缺點的結合,它是處於兩者之間的一種容器。
deque 的特點:
(1) 隨機訪問方便,即支援[ ] 操作符和vector.at() ,但效能沒有vector 好;
(2) 可以在內部進行插入和刪除操作,但效能不及list ;
(3) 可以在兩端進行push 、 pop ;
(4) 相對於verctor 佔用更多的記憶體。
雙向佇列和向量很相似,但是它允許在容器頭部快速插入和刪除(就像在尾部一樣)
函式方法:
1.Constructors 建立一個新雙向佇列 語法: deque();//建立一個空雙向佇列 deque( size_type size );// 建立一個大小為size的雙向佇列 deque( size_type num, const TYPE &val ); //放置num個val的拷貝到佇列中 deque( const deque &from );// 從from建立一個內容一樣的雙向佇列 deque( input_iterator start, input_iterator end ); // start 和 end - 建立一個佇列, 儲存從start到end的元素。 2.Operators 比較和賦值雙向佇列 //可以使用[]操作符訪問雙向佇列中單個的元素 3.assign() 設定雙向佇列的值 語法: void assign( input_iterator start, input_iterator end); //start和end指示的範圍為雙向佇列賦值 void assign( Size num, const TYPE &val );//設定成num個val。 4.at() 返回指定的元素 語法: reference at( size_type pos ); 返回一個引用,指向雙向佇列中位置pos上的元素 5.back() 返回最後一個元素 語法: reference back();//返回一個引用,指向雙向佇列中最後一個元素 6.begin() 返回指向第一個元素的迭代器 語法: iterator begin();//返回一個迭代器,指向雙向佇列的第一個元素 7.clear() 刪除所有元素 8.empty() 返回真如果雙向佇列為空 9.end() 返回指向尾部的迭代器 10.erase() 刪除一個元素 語法: iterator erase( iterator pos ); //刪除pos位置上的元素 iterator erase( iterator start, iterator end ); //刪除start和end之間的所有元素 //返回指向被刪除元素的後一個元素 11.front() 返回第一個元素的引用8 12.get_allocator() 返回雙向佇列的配置器 13.insert() 插入一個元素到雙向佇列中 語法: iterator insert( iterator pos, size_type num, const TYPE &val ); //pos前插入num個val值 void insert( iterator pos, input_iterator start, input_iterator end ); //插入從start到end範圍內的元素到pos前面 14.max_size() 返回雙向佇列能容納的最大元素個數 15.pop_back() 刪除尾部的元素 16.pop_front() 刪除頭部的元素 17.push_back() 在尾部加入一個元素 18.push_front() 在頭部加入一個元素 19.rbegin() 返回指向尾部的逆向迭代器 20.rend() 返回指向頭部的逆向迭代器 21.resize() 改變雙向佇列的大小 22.size() 返回雙向佇列中元素的個數 23.swap() 和另一個雙向佇列交換元素 語法: void swap( deque &target );// 交換target和現雙向佇列中元素函式方法
例項:
#include <iostream> #include <deque> using namespace std; //雙端佇列,頭和尾都可以進行插入和刪除 //deque是在list和vector的 基礎上改進而來的 //所以deque兼有list和vector的特點 //列印元素 void deque_print(deque<int> &deq) { if(deq.empty()) { cout<<"deque is empty"<<endl; return; } cout<<"Array method access : "; //陣列方式訪問 for(int i=0;i<deq.size();i++) { cout<<deq[i]<<" "; } cout<<endl; //迭代器方式訪問(正向訪問) cout<<"iterator mothod access : "; for(deque<int>::iterator it=deq.begin();it!=deq.end();++it) { cout<<*it<<" "; } cout<<endl; //迭代器方式訪問(逆向訪問) cout<<"reverse_iterator mothod access : "; for(deque<int>::reverse_iterator it=deq.rbegin();it!=deq.rend();++it) { cout<<*it<<" "; } cout<<endl; } void printDeq(deque<int> &deq) { for(int i=0;i<deq.size();i++) cout<<deq[i]<<" "; cout<<endl; } //末尾新增元素 void deque_append(deque<int> &deq,int beg,int end) { for(int i=beg;i<=end;i++) deq.push_back(i); } //頭部新增元素 void deque_append_h(deque<int> &deq,int beg,int end) { for(int i=beg;i<=end;i++) deq.push_front(i); } void deque_delete(deque<int> &deq) { //末尾刪除一個元素 deq.pop_back(); //頭部刪除一個元素 deq.pop_front(); //刪除全部元素 //deq.clear(); //刪除制定位置的元素 //deq.erase(deq.begin()+3); //刪除一個區間的資料 [) 左閉右開 //deq.erase(++deq.begin(),++deq.end()) deque<int>::iterator it=deq.begin(); while(it!=deq.end()) { if(*it==3) { //刪除當前位置元素,然會下一個元素的迭代器 //如果不用it接收,則當前位置已經刪除,it此時是一個野指標 //導致錯誤。所以必須使用it接收返回的下一個指標的位置 it=deq.erase(it); it++; } else { it++; } } } void deque_modify(deque<int> &deq) { //使用[],即陣列形式修改 deq[1] = 3; //使用at方式 deq.at(2) = 3; //front() deq.front() = 3; //back() deq.back() = 3; //賦值 deque<int> deq_1; deque_append(deq_1,1,10); //區間賦值 deq.assign(deq_1.begin(),deq_1.end()); //將容器從新設定為n個元素且值都為t deq.assign(5,10); cout<<"deq.assign(5,10) :"; printDeq(deq); deq.assign(15,15); cout<<"deq.assign(15,15) :"; printDeq(deq); //將容器C所有的元素刪除,然後將另一個容器C1的元素全部賦值給C deq.clear(); deq=deq_1; cout<<"deq=deq_1 :"; printDeq(deq); cout<<"deq size :"<<deq.size()<<endl; deq.push_front(100); deq.push_back(200); deq.swap(deq_1); cout<<"deq element :"; printDeq(deq); cout<<"deq_1 element :"; printDeq(deq_1); } //建構函式 deque<int> deque_list() { //預設建構函式 deque<int> deqInt; if(deqInt.empty()) { cout<<"deqInt is empty"<<endl; } deque_append(deqInt,15,25); printDeq(deqInt); //建構函式將[begin,end)區間的 元素拷貝非本身 //區間是左閉右開 deque<int> v(deqInt.begin()+3,deqInt.end()-3); printDeq(v); //建立有n個元素的的初始化容器 deque<int> v1(5); printDeq(v1); deque<int> v2(6,8); printDeq(v2); deque<int> v3(v2); deque<int> v4=v3; printDeq(v3); printDeq(v4); return deqInt; } //元素的插入 void deque_insert(deque<int> &deq) { deque<int> deq_new; deque_append(deq_new,30,40); printDeq(deq_new); //插入是在pos位置之前的位置插入的 //例如,在begin位置,就是插在begin之前的位置 deq_new.insert(deq_new.begin(),1024); printDeq(deq_new); //end屬於越尾指標 deq_new.insert(deq_new.end(),2048); printDeq(deq_new); deq_new.insert(deq_new.begin(),3,4072); printDeq(deq_new); printDeq(deq); deq.insert(deq.begin()+3,deq_new.begin(),deq_new.end()); printDeq(deq); } int main() { deque<int> deq; for(int i=0;i<10;i++)deq.push_back(i); deque_print(deq); deque_append(deq,90,100); deque_print(deq); deque_append_h(deq,-10,-5); deque_print(deq); deque_modify(deq); deque<int> deqInt=deque_list(); deque_insert(deq); return 0; }View Code
三者比較
vector 是一段連續的記憶體塊,而deque 是多個連續的記憶體塊, list 是所有資料元素分開儲存,可以是任何兩個元素沒有連續。vector 的查詢效能最好,並且在末端增加資料也很好,除非它重新申請記憶體段;適合高效地隨機儲存。
list 是一個連結串列,任何一個元素都可以是不連續的,但它都有兩個指向上一元素和下一元素的指標。所以它對插入、刪除元素效能是最好的,而查詢效能非常差;適合大量地插入和刪除操作而不關心隨機存取的需求。
deque 是介於兩者之間,它兼顧了陣列和連結串列的優點,它是分塊的連結串列和多個數組的聯合。所以它有被list好的查詢效能,有被vector好的插入、刪除效能。如果你需要隨即存取又關心兩端資料的插入和刪除,那麼deque是最佳之選。
1.2 關聯容器
set, multiset, map, multimap 是一種非線性的樹結構,具體的說採用的是一種比較高效的特殊的平衡檢索二叉樹—— 紅黑樹結構。
set 和 map 支援唯一關鍵詞(unique key),就是對每個 KEY,最多隻儲存一個元素(資料記錄)。multiset 和 multimap 則支援相同關鍵詞(equal key),這樣可有很多個元素可以用同一個 KEY 進行儲存。set(multiset)和 map(multimap)之間的區別在於 set(multiset)中的儲存資料內含了 KEY 表示式;而 map(multimap)則將 Key 表示式和對應的資料分開存放
set 又稱集合,實際上就是一組元素的集合,但其中所包含的元素的值是唯一的,且是按一定順序排列的,集合中的每個元素被稱作集合中的例項。因為其內部是通過連結串列的方式來組織,所以在插入的時候比vector 快,但在查詢和末尾新增上比vector 慢。
multiset 是多重集合,其實現方式和set 是相似的,只是它不要求集合中的元素是唯一的,也就是說集合中的同一個元素可以出現多次。
map 提供一種“鍵- 值”關係的一對一的資料儲存能力。其“鍵”在容器中不可重複,且按一定順序排列(其實我們可以將set 也看成是一種鍵- 值關係的儲存,只是它只有鍵沒有值。它是map 的一種特殊形式)。由於其是按連結串列的方式儲存,它也繼承了連結串列的優缺點。
multimap 和map 的原理基本相似,它允許“鍵”在容器中可以不唯一。
關聯容器的特點是明顯的,相對於順序容器,有以下幾個主要特點:
1、 其內部實現是採用非線性的二叉樹結構,具體的說是紅黑樹的結構原理實現的;
2、 set 和map 保證了元素的唯一性, mulset 和mulmap 擴充套件了這一屬性,可以允許元素不唯一;
3、 元素是有序的集合,預設在插入的時候按升序排列。
基於以上特點,
1、 關聯容器對元素的插入和刪除操作比vector 要快,因為vector 是順序儲存,而關聯容器是鏈式儲存;比list 要慢,是因為即使它們同是鏈式結構,但list是線性的,而關聯容器是二叉樹結構,其改變一個元素涉及到其它元素的變動比list 要多,並且它是排序的,每次插入和刪除都需要對元素重新排序;
2、 關聯容器對元素的檢索操作比vector 慢,但是比list 要快很多。 vector 是順序的連續儲存,當然是比不上的,但相對鏈式的list 要快很多是因為list 是逐個搜尋,它搜尋的時間是跟容器的大小成正比,而關聯容器 查詢的複雜度基本是Log(N) ,比如如果有1000 個記錄,最多查詢10 次, 1,000,000 個記錄,最多查詢20 次。容器越大,關聯容器相對list 的優越性就越能體現;
3、 在使用上set 區別於vector,deque,list 的最大特點就是set 是內部排序的,這在查詢上雖然遜色於vector ,但是卻大大的強於list 。
4、 在使用上map 的功能是不可取代的,它儲存了“鍵- 值”關係的資料,而這種鍵值關係採用了類陣列的方式。陣列是用數字型別的下標來索引元素的位置,而map 是用字元型關鍵字來索引元素的位置。在使用上map 也提供了一種類陣列操作的方式,即它可以通過下標來檢索資料,這是其他容器做不到的,當然也包括set 。( STL 中只有vector 和map 可以通過類陣列的方式操作元素,即如同ele[1] 方式)
Sets & MultiSets
集合(Set)是一種包含已排序物件的關聯容器。 多元集合(MultiSets)和集合(Sets)相像,只不過支援重複物件,其用法與set基本相同。
1.begin() 返回指向第一個元素的迭代器 2.clear() 清除所有元素 3.count() 返回某個值元素的個數10 4.empty() 如果集合為空,返回true 5.end() 返回指向最後一個元素的迭代器 6.equal_range() 返回第一個>=關鍵字的迭代器和>關鍵字的迭代器 語法: pair <iterator,iterator>equal_range( const key_type &key ); //key是用於排序的關鍵字 Set<int> ctr; 例如: Pair<set<int>::iterator,set<int>::iterarot>p; For(i=0;i<=5;i++) ctr.insert(i); P=ctr.equal_range(2); 那麼*p.first==2;*p.second==3; 7.erase() 刪除集合中的元素 語法: iterator erase( iterator i ); //刪除i位置元素 iterator erase( iterator start, iterator end ); //刪除從start開始到end(end為第一個不被刪除的值)結束的元素 size_type erase( const key_type &key ); //刪除等於key值的所有元素(返回被刪除的元素的個數) //前兩個返回第一個不被刪除的雙向定位器,不存在返回末尾 //第三個返回刪除個數 8.find() 返回一個指向被查詢到元素的迭代器 語法: iterator find( const key_type &key ); //查詢等於key值的元素,並返回指向該元素的迭代器; //如果沒有找到,返回指向集合最後一個元素的迭代器 9.get_allocator() 返回集合的分配器 10.insert() 在集合中插入元素 語法: iterator insert( iterator i, const TYPE &val ); //在迭代器i前插入val void insert( input_iterator start, input_iterator end ); //將迭代器start開始到end( end不被插入) 結束返回內的元素插入到集合中 pair insert( const TYPE &val ); //插入val元素, 返回指向該元素的迭代器和一個布林值來說明val是否成功被插入 //應該注意的是在集合(Sets中不能插入兩個相同的元素) 11.lower_bound() 返回指向大於(或等於)某值的第一個元素的迭代器 語法: iterator lower_bound( const key_type &key ); //返回一個指向大於或者等於key值的第一個元素的迭代器 12.key_comp() 返回一個用於元素間值比較的函式 語法: key_compare key_comp(); //返回一個用於元素間值比較的函式物件 13.max_size() 返回集合能容納的元素的最大限值11 14.rbegin() 返回指向集合中最後一個元素的反向迭代器 示例: Set<int> ctr; Set<int>::reverse_iterator rcp; For(rcp=ctr.rbegin();rcp!=ctr.rend();rcp++) Cout<<*rcp<<” ”; 15.rend() 返回指向集合中第一個元素的反向迭代器 16.size() 集合中元素的數目 17.swap() 交換兩個集合變數 語法: void swap( set &object ); //交換當前集合和object集合中的元素 18.upper_bound() 返回大於某個值元素的迭代器 語法: iterator upwer_bound( const key_type &key ); //返回一個指向大於key值的第一個元素的迭代器 1