1. 程式人生 > >STL詳解

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