stl容器--總結

原文地址:

http://blog.csdn.net/nishijibama/article/details/11852523


STL主要包含容器、算法、迭代器三大核心部分;
序列式容器中的元素順序與元素值無關,只與元素插入的次序和存放位置有關;三種序列式容器,即Vectors(向量)、Deque(雙向隊列)和List(雙向鏈表)。
vector:向量容器;
關聯式容器中的元素位置是按元素值的大小自動排序的,缺省情況下爲升序排列。其元素順序與元素值有關,與元素插入的先後次序無關。關聯式容器的底層實現是二叉搜索樹的形式。關聯式容器有SetsMultisets(集合與多重集合)以及MapMultimap(映射與多重映射) 。
 

 
迭代器
容器負責存儲數據元素,算法負責加工處理數據元素,那麼是誰負責將數據元素從容器裏提取出來交給算法去處理呢?又是誰負責將算法處理過的數據元素寫入容器呢?這項任務由迭代器負責完成。迭代器將容器與算法耦合起來,通過迭代器可以對容器內任意位置上的元素進行定位和存取(access)。
可以把迭代器看作一個類似於指向容器中元素的被泛化了的普通指針。可以像遞增一個指針那樣遞增迭代器,使其依次指向容器中每一個後繼元素;反之,也可以像遞減一個指針那樣遞減迭代器,使其依次指向容器中每一個前驅元素。儘管迭代器類似於指針,但迭代器決不是普通的指針。它們的區別是:指針是指向它所要訪問的數據元素的地址,而迭代器是指向它所要訪問的數據元素的位置(即容器的某個位置的下標)。但它們也有相同之處,即要訪問它們所指的數據元素時,都是在其前面綴“*”運算符
 
容器負責存儲數據元素,算法負責處理數據元素。而迭代器則負責從容器中存取一個個數據元素提交給算法去進行處理,或者將算法處理完的數據元素放入容器。因此,迭代器將算法和容器連在一起.STL把迭代器作爲算法的參數而非把容器直接作爲算法的參數。函數對象也可作爲算法的參數。
 
各類容器的共性——各類容器一般來說都有下列成員函數,並且,由於它們都重載了比較運算符,所以它們都允許比較判斷運算(設c1和c2是兩個類型相容的容器對象) :
·默認構造函數:    提供容器默認初始化構造函數
·複製構造函數:    將容器初始化爲現有同類容器副本的構造函數
·析構函數:         不再需要容器時進行內存整理的析構函數
·c1.empty():      若c1內沒有數據元素存在返回true,否則返回false
· c1.max_size():  返回c1中的最大元素個數
· c1.size():       返回c1中當前實有元素個數
· c1.swap(c2):    交換c1和c2的元素
 
下面的比較運算均返回true或者false:
·c1 = c2
·c1 < c2
·c1 <= c2
· c1 > c2
· c1 >= c2
· c1 == c2
· c1 != c2
容器類似於類,如容器vector(類) 內部包含一個動態數組,一個指向這個數組位置的迭代器(指針)(迭代器的定義爲: vector<int>::iterator i),還有類vector 內部的一些函數方法vecList.clear(),vecList.erase(position),vecList.insert(position, elem) ,vecList.push_back(elem)。容器list內部包含一個雙向鏈表,一個指向這個數組的迭代器(指針),還有類 內部的一些函數方法。算法則是獨立於容器的全局函數。算法直接操作迭代器。(藍色字體部分純屬個人理解,便於自己理解記憶,不一定對,請注意。)
vector類內嵌了一個iterator類,它是vector類的一個public成員。通過iterator類可以聲明向量容器的一個或多個迭代器,例如語句:
vector<int>::iterator intVeciter;  將intVecIter聲明爲int類型的向量容器迭代器。
for (intVecIter = intList.begin(); intVecIter != intList.end();++intVecIter);
向量容器迭代器的運算表達式
++intVecIter     將迭代器intVecIter前移一個位置,使其指向容器中的下一個元素;
--intVecIter      將迭代器intVecIter後移一個位置,使其指向容器中的前一個元素;
*intVecIter       返回當前迭代器位置上的元素值。
容器的成員函數begin()和end()
   不僅僅向量容器,所有容器都包含成員函數begin()和end()。函數begin()返回容器中第一個元素的位置;函數end()返回容器中最後一個元素的下一個位置。這兩個函數都沒有參數。
 
Deque雙端隊列
可以在其頭尾兩端插入或刪除元素,而且十分迅速.push_front().也可以使用成員函數push_back()在deque尾部追加元素;vector並未提供在vector頭部添加元素的push_front()成員函數。
Deque是雙向隊列,操作Deque的成員函數如下:

函    數

描      述

備註

assign(beg,end)

assign(n,elem)

給[beg; end) 區間賦值

將n個elem 的拷貝賦值

 

at(idx)

傳回索引 idx 所指的數據,若idx 越界,拋出out_of_range

 

back()

傳回最後一個數據,不檢查這個數據是否存在

 

begin()

傳回迭代器重的可一個數據

 

clear()

移除容器中所有數據

 

deque c1(c2)

deque c(n)

deque c(n, elem)

deque c(beg,end)

~deque()

複製一個deque

創建一個deque,含有n個數據,數據均已缺省構造產生

創建一個含有n個elem 拷貝的 deque

創建一個以[beg;end)區間的 deque

銷燬所有數據,釋放內存

構造函數

構造函數

構造函數

構造函數

析構函數

empty()

判斷容器是否爲空

 

end()

指向迭代器中的最後一個數據地址

 

erase(pos)

erase(beg,end)

刪除pos位置的數據,傳回下一個數據的位置

刪除[beg,end)區間的數據,傳回下一個數據的位置

 

front()

傳回地一個數據

 

get_allocator

使用構造函數返回一個拷貝

 

insert(pos,elem)

insert(pos,n,elem)

insert(pos,beg,end)

在pos位置插入一個elem拷貝,傳回新數據位置

在pos位置插入n 個elem數據,無返回值

在pos位置插入在[beg,end)區間的數據,無返回值

 

max_size()                          

返回容器中最大數據的數量

 

pop_back()

刪除最後一個數據

 

pop_front()

刪除頭部數據

 

push_back(elem)

在尾部加入一個數據

 

push_front(elem)

在頭部插入一個數據

 

rbegin()

傳回一個逆向隊列的第一個數據

 

rend()

傳回一個逆向隊列的最後一個數據的下一個位置

 

resize(num)

重新指定隊列的長度

 

size()

返回容器中實際數據的個數

 

c1.swap(c2)

swap(c1,c2)

將 c1 和 c2 元素互換

同上操作

 

operator []

返回容器中指定位置的一個引用

 

Lists

List是(帶頭結點的)雙向鏈表(doublelinked list),它是一種雙線性列表。只能順序訪問它(從前向後或者從後向前逐個訪問)。
List與向量和雙端隊列兩種容器類有一個明顯的區別就是:它不支持隨機訪問(所以沒有成員函數 at(pos)和operator[])。要訪問list中某個數據元素只能從表頭或表尾處開始循環進行(可以通過迭代器來雙向遍歷讀取)。list容器不提供對capacity()和內存維護相關的接口,因爲list容器中的每個元素分配自己的內存空間,當元素刪除時,其對應的內存空間自動銷燬。List的優勢是可以在其任何位置上插入或刪除元素,而且比較迅速(不需要移動元素)。若程序中使用List容器,那麼,必需先包含<list>頭文件。
 

關聯式容器

對於關聯式容器,一是要注意這類容器具有自動排序能力,即它們內部的元素總是時刻自動有序的;二是要注意使用Set(集合) 和Multisets(多重集合)之前,必須先包含頭文件<set>,而使用Map 和Multimap之前,必須先包含頭文件<map>;三是要注意它們支持雙向迭代器,但不支持隨即迭代器,因此不能隨即存取其元素。
一個集合(set)是一個容器,其所包含的元素的值是唯一的,而Multisets所包含的元素的值可以不唯一(即可以有重複元素)。注意,集合(set)與多重集合(Multisets)的內部數據組織是一顆紅黑樹(一種非嚴格意義上的平衡二叉樹),這顆樹具有對數據自動排序的功能,因此,set和Mutilset內部的所有數據元素是自動有序的。
    map(映射)是經過排序的二元組集合,map中的每個元素都是由兩個值組成,其中key鍵值必須是唯一,而另一個值是該元素關聯的數值。multimap(多重映射)和映射(map)相似,但其中的鍵值可以有重複值。map與multimap內部數據的組織也是一顆紅黑樹,所以它們內部所有的數據元素也都是自動有序的。
 
操作Set 和Multisets的成員函數及算法 
 (1)構造函數
定義一個元素爲整數的集合intSet,可以用:
                   set<int>  intSet;
 (2) 數據元素基本操作方面的成員函數
Ø       插入元素i:intSet.insert(i);
Ø       刪除元素i(如果存在):intSet.erase(i);
Ø       判斷元素i是否屬於集合: if (intSet.find(i) != intSet.end()) ...
Ø       返回集合元素的個數:intSet.size();
Ø       判斷集合是否空:bool empty() const;
Ø       將集合清爲空集:intSet.clear()。 
 
    問題:如何判斷一個元素是否在Set中?
方法1:用成員函數count()來判定關鍵字是否出現,若出現返回值是1,否則返回0。比如查找關鍵字1是否出現:
               int index = intSet. count(1);
    方法2:用成員函數find()來定位數據元素出現位置,它返回一個迭代器,當set中包含該數據元素時,返回數據元素所在位置的迭代器;如果不包含,返回的迭代器等於end函數返回的迭代器。比如查找關鍵字1是否出現:
              if (intSet.find(1) == intSet.end())     
                  cout<<”Not Find ”<<1<<endl;
              else
                       cout<<” Find value ”<<1<<endl;
 
(3) 遍歷方面的成員函數
·iterator begin();      //返回首元素的迭帶器指針
·iterator end();        //返回尾元素後的迭帶器指針,而不是尾元素的迭帶器指針
·reverse_iterator rbegin();    //返回尾元素的逆向迭帶器指針,用於逆向遍歷容器
·reverse_iterator rend();      //返回首元素前的逆向迭帶器指針,用於逆向遍歷容器
 
(4) 其它操作方面的成員函數
·const_iterator lower_bound(const Key& key);      //返回容器元素等於key迭代指針,//否則返回end()
·const_iterator upper_bound(const Key& key);
·int count(const Key& key) const;      //返回容器中元素值等於key的元素個數
·pair<const_iterator , const_iterator> equal_range(const Key& key) const;    // 返回//容器中元素值等於key的迭代指針[first, last)
·const_iterator find(const Key& key) const; //查找功能返回元素值等於key迭代器指針
·void swap(set& s);       //交換單集合元素
·void swap(multiset& s);       //交換多集合元素
 
(5) 集合的算法
 STL中的算法包含在 <algorithm> 頭文件中,集合的算法也包含在該頭文件中。
Ø       集合的並:set_union
Ø       集合的交:set_intersection
Ø       集合的差:set_difference
 
 
操作Maps和Multimaps的成員函數  
map是STL的一個關聯容器,它提供一對一(其中第一個可以稱爲關鍵字,每個關鍵字只能在map中出現一次,第二個可稱爲該關鍵字對應的值,即即一個數據元素爲key/ value對的形式)的數據處理能力。map內部數據的組織也是一棵紅黑樹,所以map內部所有的數據元素都是自動有序的。
那麼什麼是一對一的數據映射?比如一個班級中,每個學生的學號跟他的姓名就存在着一一映射的關係,這個模型用map可以輕易描述,很明顯學號用int描述,姓名用char *字符串描述,如下面的map描述代碼:
             map<int, char*>  mapStudent;
一、STL中的通用工具pair與make_pair的用法
C++ STL預定義了幾個通用工具類(或結構體)、函數等,結構體pair與函數make_pair()就是其中的兩個。它們定義在通用工具頭文件<utility>內,使用它們之前必需包含此頭文件。
1、pair的應用
Pair的用途是將2個數據構造爲一個數據元素(通過其構造函數)。當有這樣的需求時就可以使用pair來構造,如STL中的容器map就是將key和value放在一起作爲一個元素來保存的,因此,構造map的數據元素時就要用到pair。pair的另一個應用是,當一個函數需要返回2個值的時候,可以選擇pair這種數據類型。 pair的實現是一個結構體,主要的兩個成員變量是first 和second。 因定義pair是使用struct不是class,所以可以直接使用pair的成員變量(因爲struct的成員都是public成員)。
 
std::pair模型:
template  <class T1,  class T2>   
           struct  pair   
           {   
               T1   first;       //key值部分
               T2   second;   //value部分
   
pair(const T1  a, const T2  b) : first(a), second(b) { } //構造函數
            };   
如:std::pair<int, float>(3, 1.1);將構造一個視爲一個數據元素整體來對待的數對(3,1.1),其中3爲int型,1.1爲float型。
 
1、 便捷函數make_pair()
    便捷函數make_pair()的作用也是將2個數據構造爲一個數據元素(make_pair()的返回值)。make_pair()的函數模版聲明如下:
 
    template <class T1, class T2>
pair<T1,T2>  make_pair (const T1  x, const T2  y)
{
return pair<T1,T2>(x, y);  //調用pair類的構造函數
}
 
例如:std::make_pair(42,‵@′);與std::pair<int, char>(42,‵@′);都是返回一個由(42,‵@′)組成的數對(42,‵@′),顯然,std::make_pair(42,‵@′)更簡便,而pair在有些編譯器下不允許這麼簡便寫。
二、操作Maps和Multimaps的成員函數
(1)構造函數
map共提供了6個構造函數,通常用圖2-13所示的方法構造一個Map。
 
 
                         圖2-13  
構造map的常用方法
(2)數據的插入的三種方法
第一種方法:用insert函數插入pair數據
     mapStudent.insert(pair<int, char*>(1, “student_one”));
第二種方法:用insert函數插入value_type數據
     mapStudent.insert(map<int, char*>::value_type(1, “student_one”));
第三種方法:用數組方式插入數據
     mapStudent[1] =  “student_one”;
     mapStudent[2] =  “student_two”;
 
(3) 數據的刪除
   //如果要刪除數據元素1,用迭代器刪除
       map<int, string>::iterator iter;
       iter = mapStudent.find(1);
       mapStudent.erase(iter);
 
(4) 數據的查找
第一種方法:用count函數來判定關鍵字是否出現,返回1,元素存在;否則返回1。比如查找關鍵字1是否存在?
        int index = mapStudent. count(1);
第二種方法:用find函數來定位數據出現位置,它返回的一個迭代器,當數據出現時,它返回數據所在位置的迭代器,如果map中沒有要查找的數據,它返回的迭代器等於end函數返回的迭代器:
       map<int, char*>::iterator iter; 
       iter = mapStudent.find(1);
       if (iter != mapStudent.end())
                  cout<<”Find, the value is ”<<iter->second<<endl;
       else
                  cout<<”Do not Find”<<endl;
 
 (5) 數據的其它基本操作
Ø       返回map元素的個數: mapStudent.size();
Ø       將map清爲空集:mapStudent.clear() ;
Ø       判斷map是否爲空:mapStudent.Empty() ;
Ø       數據的遍歷:比如用數組方式
       int nSize = mapStudent.size();
       for(int nIndex = 0; nIndex < nSize; nIndex++)
       {  
cout<<mapStudent[nIndex]<<end; 
}
 
 (6)其它)操作函數
·void swap(map& s);        //交換單映射元素
·void swap(multimap& s);   //交換多映射元素
 
(7)特殊函數
·reference operator[](const Key& k);   //僅用在單映射map類中,可以以數組的形式//給映射添加鍵---值對,並可返回值的引用
 
 
vector,list,deque,set,map of STL[轉載]
List封裝了鏈表,Vector封裝了數組, list和vector得最主要的區別在於vector使用連續內存存儲的,他支持[]運算符,而list是以鏈表形式實現的,不支持[]。

Vector對於隨機訪問的速度很快,但是對於插入尤其是在頭部插入元素速度很慢,在尾部插入速度很快。List對於隨機訪問速度慢得多,因爲可能要遍歷整個鏈表才能做到,但是對於插入就快的多了,不需要拷貝和移動數據,只需要改變指針的指向就可以了。另外對於新添加的元素,Vector有一套算法,而List可以任意加入。
Map,Set屬於標準關聯容器,使用了非常高效的平衡檢索二叉樹:紅黑樹,他的插入刪除效率比其他序列容器高是因爲不需要做內存拷貝和內存移動,而直接替換指向節點的指針即可。
Set和Vector的區別在於Set不包含重複的數據。Set和Map的區別在於Set只含有Key,而Map有一個Key和Key所對應的Value兩個元素。
Map和Hash_Map的區別是Hash_Map使用了Hash算法來加快查找過程,但是需要更多的內存來存放這些Hash桶元素,因此可以算得上是採用空間來換取時間策略。
vector - 會自動增長的數組
vector又稱爲向量數組,他是爲了解決程序中定義的數組是不能動態改變大小這個缺點而出現的。一般程序實現是在類創建的時候同時創建一個定長數組,隨着數據不斷被寫入,一旦數組被填滿,則重新開闢一塊更大的內存區,把原有的數據複製到新的內存區,拋棄原有的內存,如此反覆。

由於程序自動管理數組的增長,對於我們程序員來說確實輕鬆了不少,只管把數據往裏面插就行了,當然把物理內存和虛擬內存插爆掉了就是操作系統來找你麻煩了:-)
vector由於數組的增長只能向前,所以也只提供了後端插入和後端刪除,也就是push_back和pop_back。當然在前端和中間要操作數據也是可以的,用insert和erase,但是前端和中間對數據進行操作必然會引起數據塊的移動,這對性能影響是非常大的。對於所有數組來說,最大的優勢就是隨機訪問的能力。
在vector中,提供了at和[]運算符這兩個方法來進行隨機訪問。由於每個數據大小相同,並且無間隔地排列在內存中,所以要對某一個數據操作,只需要用一個表達式就能直接計算出地址:address = base + index * datasize同樣,對vector進行內存開闢,初始化,清除都是不需要花大力氣的,從頭到尾都只有一塊內存。list - 擅長插入刪除的鏈表有黑必有白,世界萬物都是成對出現的。鏈表對於數組來說就是相反的存在。數組本身是沒有動態增長能力的(程序中也必須重新開闢內存來實現),而鏈表強悍的就是動態增長和刪除的能力。但對於數組強悍的隨機訪問能力來說的話,鏈表卻很弱。list是一個雙向鏈表的實現。爲了提供雙向遍歷的能力,list要比一般的數據單元多出兩個指向前後的指針。這也是沒辦法的,畢竟現在的PC內存結構就是一個大數組,鏈表要在不同的環境中實現自己的功能就需要花更多空間。
list提供了push_back,push_front,pop_back,pop_front四個方法來方便操作list的兩端數據的增加和刪除,不過少了vector的at和[]運算符的隨機訪問數據的方法。並不是不能實現,而是list的設計者並不想讓list去做那些事情,因爲他們會做得非常差勁。對於list來說,清除容器內所有的元素是一件苦力活,因爲所有數據單元的內存都不連續,list只有一個一個遍歷來刪除。 deque - 擁有vector和list兩者優點的雙端隊列黑與白,處於這兩個極端之間的就是令人愉悅的彩色了。deque作爲vector和list的結合體,確實有着不凡的實力。STL的deque的實現沒有怎麼去看過,不過根據我自己的猜測,應該是把數組分段化,在分段的數組上添加指針來把所有段連在一起,最終成爲一個大的數組。
deque和list一樣,提供了push_back,push_front,pop_back,pop_front四個方法。可以想象,如果要對deque的兩端進行操作,也就是要對第一段和最後一段的定長數組進行重新分配內存區,由於分過段的數組很小,重新分配的開銷也就不會很大。deque也和vector一樣,提供了at和[]運算符的方法。要計算出某個數據的地址的話,雖然要比vector麻煩一點,但效率要比list高多了。首先和list一樣進行遍歷,每次遍歷的時候累積每段數組的大小,當遍歷到某個段,而且baseN <= index < baseN + baseN_length的時候,通過address = baseN + baseN_index就能計算出地址由於分過段的後鏈表的長度也不是很長,所以遍歷對於整體性能的影響就微乎其微了。
看起來deque很無敵吧,不過deque和希臘神話的阿吉里斯一樣,再怎麼強大也是有自己的弱點的,之後的測試數據中就能看到了。
 
 
 

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