順序容器
c++中順序容器包含三種方法:
- verctor (可以簡單的看作是可以動態(tài)調(diào)控大小的數(shù)組)
- list (可以簡單的看作是雙向鏈表)
- deque(可以簡單的看作是雙向隊列)
1、三種容器的異同
vector:表示一段連續(xù)的內(nèi)存區(qū)域怀各,每個元素被順序存儲在這段內(nèi)存中。
對vector其的訪問效率很高恋追,但是插入和刪除操作的效率很低,因為它要像數(shù)組那樣對其他的數(shù)據(jù)進行拷貝、移位。它的動態(tài)擴容實現(xiàn)的原理是當容量不夠時把整體全部數(shù)據(jù)拷貝到另一個全新的分配容量充足區(qū)域。
list:表示非連續(xù)的內(nèi)存區(qū)域 并通過一對指向首尾元素的指針雙向鏈接起來 從而允許向前和向后兩個方向進行遍歷
在 list 的任意位置插入和刪除元素的效率都很高浙巫,指針的指向關系必須被重新賦值, 但是不需要用拷貝元素來實現(xiàn)移動刷后。但是另一方面 它對隨機訪問的支持并不好訪問一個元素需要遍歷中間的元素 另外 每個元素還有兩個指針的額外空間開銷。
deque:也表示一段連續(xù)的內(nèi)存區(qū)域 但是 與 vector 不同的是 它支持高效地在其首部插入和刪除元素渊抄。
它通過兩級數(shù)組結構來實現(xiàn)尝胆,一級表示實際的容器 ,第二級指向容器的首和尾护桦。
2含衔、三種容器選擇的準則:
- 如果我們需要隨機訪問一個容器 則 vector 要比 list 好得多
- 如果我們已知要存儲元素的個數(shù) 則 vector 又是一個比 list 好的選擇
- 如果我們需要的不只是在容器兩端插入和刪除元素 則 list 顯然要比 vector 好
- 除非我們需要在容器首部插入和刪除元素 否則 vector 要比 deque 好
3、 定義一個順序容器
為了定義一個容器對象 我們必須先包含相關聯(lián)的頭文件二庵,應該是下列頭文件之一:
#include <vector>
#include <list>
#include <deque>
#include <map> //映射容器(關聯(lián)容器)
#include <set> //集合容器(關聯(lián)容器)
定義一個容器
容器對象的定義以容器類型的名字開始 后面是所包含的元素的實際類型:
vector< string > svec;
list< int > ilist;
上述定義了兩個空的容器贪染,也可以為容器指定顯示的長度。
const int list_size = 64;
list< int > ilist( list_size );
初始化容器
- 那么下面需要對其進行賦值初始化催享,可以調(diào)用push_back()方法向容器中插入元素杭隙,它將元素插入在容器的尾部(list 和 deque 容器也支持 push_front() 它把新元素插入在鏈表的前端):
string text_word;
while ( cin >> text_word ) //linux 中跳出次循環(huán)的條件是輸入錯誤或者按鍵 Ctrl + D
svec.push_back( text_word );
- 我們還可以指定一個容器長度,并指定一個值用它來初始化每個元素因妙。指定長度之后我們可以利用resize()方法對其重新規(guī)劃容量痰憎,而且可以為新規(guī)劃的元素初始化為特定的值;
list< int > ilist( list_size, - 1 ); //第一個參數(shù)是容器的顯式長度攀涵,第二個是初始化的每個元素的值
vector< string > svec( 24, "pooh" ); //第一個參數(shù)是容器的顯式長度铣耘,第二個是初始化的每個元素的值
svec.resize( 2 * svec.size(), "piglet" ); //將容量重新規(guī)劃成原來的兩倍,并將新規(guī)劃的部分用 “piglet” 進行初始化
- 我們也可以用一個現(xiàn)有的容器對象初始化一個新的容器對象
vector< string > svec2( svec );
list< int > ilist2( ilist );
容器之間的關系操作符(== ,>,>=,<,<=, !=)
- 如果所有元素相等而且兩個容器含有相同數(shù)目的元素 則兩個容器相等
- 第一個不相等元素的比較決定了兩個容器的小于或大于關系
- 如果所有元素相同但是長度不等以故,短的容器 < 長的容器
注意:我們能夠定義的容器的類型有三個限制(實際上蜗细,它們只適用于用戶定義的類類型)
- 元素類型必須支持等于操作符;
- 元素類型必須支持小于操作符 (前面討論的所有關系操作符都用這兩個操作符來實現(xiàn) );
- 元素類型必須支持一個缺省值( 對于類類型,即指缺省構造函數(shù));
4、順序容器的操作
插入
方法主要有兩種
- push_back(value)
從容器的尾部插入一個元素怒详,value的類型需要與容器初始化的類型一致
- insert()
有三種應用的形式:1炉媒、在特定的位置插入元素;2棘利、在某個位置插入指定數(shù)量的元素橱野;3、 向容器某個位置插入一段范圍內(nèi)的元素
// 等價的尾部插入元素
slist.push_back( value );
slist.insert( slist.end(), value );
//在某個位置插入元素善玫,把spouse插入到Danny前面
string son( "Danny" );
list<string>::iterator iter;
iter = find( slist.begin(), slist.end(), son );
slist.insert( iter, "spouse" );
//在某個位置插入指定數(shù)量的元素水援,在容器首部插入10個Anna
vector<string> svec;
string anna( "Anna" );
svec.insert( svec.begin(), 10, anna );
向容器某位置插入一段范圍內(nèi)的元素
string sarray[4] = { "quasi", "simba", "frollo", "scar" };
svec.insert(svec.begin(),sarray,sarray+4); //sarray+4是指從string數(shù)組第0個元素開始至第四個元素之間的元素密强,不包括下界。
刪除
方法也是兩種
pop_back()從尾部刪除元素蜗元,刪除容器里面的末尾元素或渤,它不返回元素 只是簡單地刪除它。
erase() 兩種應用形式:1奕扣、刪除特定位置的單個元素 2薪鹦、刪除由一對
iterator 標記的一段范圍內(nèi)的元素
string searchValue( "Quasimodo" );
list<string>::iterator iter = find( slist.begin(), slist.end(), searchValue );
if ( iter != slist.end() )
slist.erase( iter );
slist.erase( slist.begin(), iter );
slist.erase( slist.begin(), slist.end() );
賦值和對換
賦值操作符使用針對容器元素類型的賦值操作符 把右邊容器對象中的元素依次拷貝到左邊的容器對象中。對換的操作有swap()方法惯豆,這是將兩個容器的內(nèi)容進行互換池磁。
// slist1 含有 10 個元素
// slist2 含有 24 個元素
// 賦值之后都含有 24 個元素,反之則都含有10個元素
slist1 = slist2;
//對換過后slist1含有24個元素,ilist2含有10個元素楷兽。
slist1.swap( slist2 );
- 賦值還有一個assign()方法相關的一些說明參照官方手冊https://zh.cppreference.com/w/cpp/container/vector
泛型算法
從概念上講地熄,我們的思想是把所有容器類型的公共操作抽取出來,形成一個通用算法集合芯杀,它能夠被應用到全部容器類型以及內(nèi)置數(shù)組類型上 端考。這組通用算法被稱作泛型算法。下面主要將find()操作
#include <list>
#include <vector>
// the associated header file
#include <algorithm>
int ia[ 6 ] = { 0, 1, 2, 3, 4, 5 };
vector<string> svec;
list<double> dlist;
vector<string>::iterator viter;
list<double>::iterator liter;
int *pia;
// 如果找到, find()返回指向該元素的 iterator
// 對于數(shù)組, 返回指針
pia = find( &ia[0], &ia[6], some_int_value );
liter = find( dlist.begin(), dlist.end(), some_double_value );
viter = find( svec.begin(), svec.end(), some_string_value );