C++ 順序容器 筆記整理
順序容器
順序容器型別
vector
可變大小陣列.支援快速隨機訪問.在尾部之外的位置插入or刪除元素可能很慢.
deque
雙端佇列. 支援快速隨機訪問. 從頭尾位置插入or刪除元素很快.
list
雙向連結串列. 只支援雙向順序訪問. 在`list中任何位置進行插入or刪除操作都比較快.
array
固定大小陣列.支援快速隨機訪問. 不能新增or刪除元素.
string
與 vector
類似. 隨機訪問快.在尾部插入or刪除快.
容器操作
// 建構函式
C c; // 預設建構函式,構造空容器
C c1(c2); // 構造c2的拷貝c1
C c(b,e); // 構造c,將迭代器b和e指定範圍內的元素拷貝到c (array不支援)
C c{a,b,c}; // 列表初始化{}
// 賦值與swap
c1=c2; // 將c1中的元素替換c2中的元素
c1={a,b,c...}; // 將c1中的元素替換為列表中的元素! (array不支援)
a.swap(b); // 交換a,b的元素
swap(a,b); // 與a.swap(b)等價
// 大小
c.size(); // c中元素的數目 (不支援 forward_list)
c.max_size(); // c可儲存的最大元素數目
c.empty(); // 若c中儲存元素,返回false.否則為true
// 新增/刪除元素 (不適用於array)
// 注:在不同容器中,這些操作的介面都不同
c.insert(args); // 將args中的元素拷貝進c
c.emplace(inits); // 使用inits構造c中的一個元素
c.erase(args); // 刪除args指定元素
c.claer(); // 刪除c中所有元素,返回void
// 獲取迭代器
c.begin();c.end(); // 返回指向c的首元素和尾元素之後位置的迭代器
c.cbegin();c.cend();// 返回const_iterator
// 關係運算符
== != // 所有容器都支援的相等/不等 運算子
<,<=,>,>= // 關係運算符 (無序關聯容器不支援)
// 反向容器的額外成員(不支援forward_list)
reverse_iterator; // 按逆序定址元素的迭代器
const_reverse_iterator; // 不能修改元素的逆序迭代器
c.rbegin();c.rend(); // 返回指向c尾元素和首元素之前位置的迭代器
c.cebegin();c.crend(); // 返回const_reverse_iterator
迭代器
迭代器範圍由一對迭代器表示.
左閉右開 區間!
begin 與 end 相等,範圍為空
不需要寫訪問時,應使用
cbegin
與cend
容器定義與初始化
C c; // 預設建構函式.若是一個array,則c中元素按預設方式初始化.否則為空.
C c1(c2); // c1初始為為c2的拷貝.注意! 型別相同(容器型別,元素型別).對於array,大小還必須相同.
C c1=c2;
C c{a,b,c}; // 初始化為初始化列表中元素的拷貝.
C c={a,b,c};
C c(b,e); // 迭代器初始化.c初始化為b和e指定範圍中的元素的拷貝.
/* 只有順序容器(不包括array)的構造器才能大小引數 */
C seq(n); // seq包含n個元素.這些元素進行了初始化.
C seq(n,t); // 初始化了n個元素.值都為t.
賦值與swap
- 賦值相關運算會導致指向 左邊容器 的內部迭代器,引用和指標失效!
- swap操作將容器內部內容交換 不會 導致指向容器的迭代器,引用和指標失效.(容器型別為array和string的情況除外)
c1=c2; // c1中的元素替換為c2中的元素.
C={a,b,c}; // array不支援
swap(c1,c2);
c1.swap(c2);
- assign操作不適用於關聯容器和array! 僅順序容器!
seq.assign(b,e); // 迭代器b,e不能指向seq中的元素
// 由於舊元素被替換掉了,傳遞給assign的不能是呼叫的assign的容器.
seq.assign(il); // 替換為初始化列表 il 中的元素
seq.assign(n,t); // 替換為n個t值的元素
- 統一使用非成員版本的swap是一個好習慣.
swap(a,b);
向順序容器新增元素
順序容器與關聯容器的不同之處在於 二者組織元素的方式.
這些不同之處直接關係到元素如何 儲存,訪問,新增,刪除
非常重要的一點. 除了 forward_list 其他的順序容器都是前插!!!
// 這些操作會改變容器的大小.array不支援!
c.push_bacck(t); // 在c的 尾部 建立一個值為t 或由args建立 的元素.
c.emplace_back(args);
c.push_front(t); // 在c的 頭部 建立一個值為t 或由args建立 的元素.
c.emplace_front(args);
c.insert(p,t); // 在迭代器p指向的元素 之前 新增一個元素.
c.emplace(p,args);
c.insert(p,n,t); // 在p指向的元素 之前 新增 n個 值為t的元素
c.insert(p,b,e); // 將迭代器b和e指定範圍的元素插入到 p指向的元素之前!
c.insert(p,il); // il是一個花括號包圍的 元素值列表 ,將這些值插入到p指向元素之前.
forward_list
有自己專有版本的insert
和emplace
;forward_list
不支援push_back
和emplace_back
;vector
和string
不支援push_front
和emplace_front
;向一個
vector
,string
或deque
插入元素會使所有指向容器的迭代器,引用,指標失效.vector
不支援push_front
.但是 我們可以插入到begin()
之前!!!svec.insert(sevc.begin(),"hello!"); // 相當於push_front()
將元素插入到
vector
,deque
,string
中的任何位置是合法的, 但很耗時.使用
insert
的返回值,可以容器中的特定一個位置反覆的插入元素:list<stirng> lst; auto iter = lst.begin(); while(cin>>word) iter = lst.insert(iter,word); //等價與 push_front
emplace
函式是在容器中 直接構造 元素.傳遞給emplace
函式的引數必須與 元素型別的建構函式 相匹配.
訪問元素
c.back(); // 返回c中尾元素的引用;
c.front(); // 返回c中頭元素的引用;
c[n]; // 返回下標為n的元素的引用.n是一個無符號整數
c.at(n); // 返回下標為n的元素的引用. 若越界!會丟擲一個 out_of_range的異常
at
和下標操作
只適用於string vector deque array
back
不適用於forward_list
訪問成員函式返回的是 引用
若容器是一個
const
物件,則返回的是const
的引用;否則,普通引用,可用來改變元素的值;
確保下標訪問合法,可以使用
at
成員函式*iter
解引用迭代器也可以訪問元素
刪除元素
- 刪除元素會改變容器大小,不適用於 array
// 刪除元素會改變容器大小,不適用於 array
c.pop_back(); // 刪除c中尾元素,返回void
c.pop_front(); // 刪除c中首元素,返回void
c.erase(p); // 刪除迭代器p指向的元素,返回一個指向被刪除元素 之後 的迭代器!!!
c.erase(b,e); // 刪除b和e所指定範圍的元素,返回一個指向最後一個被刪除元素 之後 的迭代器!!!
c.clear(); // 刪除c中所有元素,返回void
forward_list
有特殊版本的erase
forward_list
不支援pop_back()
;vector
和string
不支援pop_front()
;刪除
deque
中 除首尾之外 的任何元素都會使所有迭代器,引用和指標失效指向
vector
和string
中 刪除點之後 的迭代器,引用,指標都會失效.刪除元素的成員函式並不會檢查其引數! 在刪除元素之前,請確保它們存在
pop_front
和pop_back
成員函式返回void,若需要彈出的值! 必須在執行彈出操作前儲存它!!!使用
front()
/back()
erase
函式返回的是 指向刪除的 最後一個元素之後 位置的迭代器
特殊的 forwartd_list
操作
為什麼
forward_list
需要特殊版本的新增和刪除操作呢?對於單鏈表來說.新增或刪除一個元素.它前面的元素內容也會發生變化.
為了刪除或新增一個元素.必須訪問到它的前驅元素.
在一個
forward_list
中新增or刪除元素的操作都是通過改變 給定元素之後的元素來完成的首前 迭代器:
before_begin
forward_list
在添刪元素之前,我們必須關注兩個迭代器! 指向我們要處理的元素 and 指向其前驅
lst.before_begin(); // 返回指向連結串列首元素之前 不存在元素 的迭代器,不能解引用!
lst.cbefore_begin();
list.insert_after(p,t); // 在迭代器p之後 的位置插入元素,t是一個物件!!!
list.insert_after(p,n,t); // n 是數量
list.insert_after(p,b,e); // b,e表示範圍的一對迭代器
list.insert_after(p,il); // il是一個花括號列表.
// 返回 一個指向最後一個插入的元素之後的迭代器
emplace_after(p,args); // 使用args在p指定位置之後 建立一個元素.返回一個指向這個新元素的迭代器!
lst.erase_after(p); // 刪除p指向位置之後的元素!
lst.erase_after(b,e); // 刪除從b之後(不包含b)到e之間的元素!
// 返回 一個指向被刪元素之後元素的迭代器!
改變容器大小
// resize 來增大或減小容器.
c.resize(n); // 調整c的大小為n個元素.
c.resize(n,t); // 調整c的大小為n.任何 新新增的 元素值為t
resize
不支援array
- 若
resize
縮小容器,則指向被刪除元素的迭代器,引用,指標都會失效! 對
vector
stirng
deque
進行resize
可能導致迭代器,指標,引用失效關於容器大小的操作.
成員函式size(); // 返回當前容器中元素的個數 成員函式empty(); // 當size為0時返回真,否則返回假 成員函式max_size(); // 返回一個大於或等於該型別容器所能容乃的最大元素的值
關係運算符
比較兩個容器實際上是進行元素的逐對比較.
vector
物件是如何增長的
元素必須是連續儲存的.如沒有新空間容納元素,容器必須 分配新的記憶體空間儲存已有元素和新元素 將已有元素從舊位置移動到新空間中,然後新增新元素,釋放舊空間!!
為了避免上一條代價,採用 減少容器空間重新分配次數的策略 !!! 通常會分配比新空間要求更大的記憶體空間!
管理容量的成員函式
// shrink_to_fit 只適用於 vector string deque // capacity 和 reserve 只適用於 vector string c.shrink_to_fit(); // 請將capacity() 減少為 size() 相同大小 c.capacity(); // 不重新分配記憶體空間的話,c可以容納多少元素 c.reserve(n); // 分配至少容納n個元素的記憶體空間
reserve
並不改變容器中元素的數量,僅影響vector
預先分配多大的記憶體空間只有當需要的記憶體空間超過當前容量時
reserve
呼叫才會改變vector
的容量capacity
是指不重新分配記憶體空間下,可以容納多少元素;size
是指它已經儲存的元素的數目;每個
vector
實現都可以選擇自己記憶體分配策略,原則: 只有當迫不得已時才會分配新的記憶體空間
額外的 string
操作
除了順序容器共同的操作外.string型別還提供了額外的一些操作.如
- string類與C風格字元陣列之間的轉換
- 增加下標替代迭代器的版本
- 大量函式
構造 string
的其他方法
// n len2 和 pos2 都是無符號值
string s(cp,n); // s是cp指向的陣列中前n個字元的拷貝.此陣列至少應該包含n個字元
string s(s2,pos2); // s是string s2 從下標pos2開始的字元的拷貝
string s(s2,pos2,len2); // s是string s2 從下標pos2開始的len2個字元的拷貝
- 通常,當從一個
const char*
建立string
時,指標指向的陣列必須是 以空字串結尾的 ,拷貝操作遇到 空字元 停止!!! 若傳遞給建構函式一個 計數值 ,陣列 不必以空字元結尾.
若未傳遞 計數值 且 未以空字元結尾.建構函式的行為 未定義
const char *cp = "hello world!!!"; // 是以空字元為結尾 char noNULL[] = {'H','i'}; // 沒有空字元結尾 string s1(cp); // s1=="hello world!!!" string s2(noNULL,2); // s2=="Hi" // 因為有len.
substr
操作!!!
substr
返回一個string
其實原始字串或部分字串的拷貝.
s.substr(pos,n); // 返回一個stirng,包含s從pos開始的n個字元的拷貝.pos預設為0,n預設為s.size();
// n是大小,長度!!!
改變 string
的其他操作
除了順序容器的賦值運算子, assign
, insert
, erase
操作
- 接受下標版本的 insert和erase版
- C風格字串陣列的insert和assign
- append 和 replace 函式
s.insert(pos,args); // 在pos之前!!! 插入args指定的字元.pos可以是下標or迭代器.
// 接受下標的返回一個指向s的引用; 接受迭代器返回指向 第一個插入字元 的迭代器
s.erase(pos,len); // 刪除從pos開始的len個字元.若len被省略,刪除從pos開始到結尾,返回s的一個引用
s.assign(args); // 將s中的字元替代為args指定的字元.返回一個指向s的引用
s.append(args); // 將args追加到s.返回一個指向s的引用
s.replace(range,args); // 刪除s中範圍range內的字元,替換為args指定字元.
// range: 1.一個下標和一個長度 2. 一對指向s的迭代器
append assign insert replace
函式有多個過載版本- 並不是每個函式都支援所有形式的引數
string
搜尋操作
- 搜尋操作返回的是一個
string::size_type
值,表示 匹配發生位置的下標 - 搜尋失敗,返回的是
string::npos
s.find(args); // 查詢s中args第一次出現的位置
s.rfind(args); // 查詢s中args最後一次出現的位置
s.find_first_of(args); // 在s中查詢args中任一字元第一次出現的位置
s.find_last_of(args); // 在s中查詢args中任一字元最後一次出現的位置
s.find_first_not_of(args); // 在s中查詢第一個不在args中的字元
s.find_last_not_of(args); // 在s中查詢最後一個不在args中的字元
//args的引數形式:
c,pos; // 從s位置pos開始查詢字元c.pos預設為0
s2,pos; // 從s中查詢s2字串
cp,pos; // 從s中pos位置開始查詢 指標cp指向的以空字元結尾的C風格字串.pos預設為0
cp,pos,n; // 從s中pos位置開始查詢cp指向的陣列的前n個字元.pos 和 n 無預設值
compare
函式
- 標準庫string提供一組
compare
函式,這些函式與C標準庫的strcmp
函式相似 s.compare
返回0 正數 負數
// s.compare()的幾種引數形式
s2; // 比較s和s2
pos1,n1,s2; // 將s中從pos1開始的n1個字元 與 s2比較
pos1,n1,s2,pos2,n2; // 將s中從pos1開始的n1個字元 與 s2從pos2開始的n2個字元 比較
cp; // 比較s與cp所指向的以空字元結尾的字元陣列
pos1,n1,cp; // 將s中從pos1開始的n1個字元 與 cp所指向的以空字元結尾的字元陣列進行比較
pos1,n1,cp,n2; // 將s中從pos1開始的n1個字元 與 cp所指向的地址開始的n2字元 比較
數值轉換
// string和數值之間的轉換
to_string(val);
stoi(s,p,b); // 返回s的起始子串(整數的內容)數值,b表示轉換所用的基數.預設是10;p是size_t指標.
stol(s,p,b);
stoul(s,p,b);
stoll(s,p,b);
stoull(s,p,b);
stof(s,p); // f返回s的起始子串(表示浮點數內容)的數值.
stod(s,p);
stold(s,p);
- 若string不能轉換為一個數值,這些函式丟擲一個 invalid_argument 異常
- 若 轉換得到的數值 無法用任何型別表示,丟擲一個 out_of_range 異常
重點!!!
to_string(val); // 將數值轉換成 string啊!!!
stoi(s); // 將string 轉成 int
容器介面卡
- 除了順序容器外,標準庫還定義了三個 順序容器介面卡
stack
queue
priority_queue
棧 / 佇列 / 優先佇列(堆)- 介面卡 是一種機制!使得某種事物的行為看起來像另一個.
// 所有容器介面卡 都支援的操作與型別
size_type; // 一種型別,足以儲存當前型別的最大物件的大小
value_type; // 元素型別
container_type; // 實現介面卡的底層容器型別
A a; // 建立一個名為a的介面卡
A a(c); // 建立一個名為a的介面卡,帶有容器c的一個拷貝
關係運算符; // == != < <= > >=
a.empty(); // 若a包含任意元素,返回false.否則 true
a.size(); // 返回a中的元素數目
swap(a,b); // 交換a b 內容,ab型別相同,包括底層容器的型別
a.swap(b);
- 所有的介面卡都要求容器具有新增 刪除元素的能力
- 介面卡不能構造在array之上,也不能用forward_list來構造介面卡
棧介面卡
// 棧操作
s.pop(); // 刪除棧頂元素,但不返回該元素值!!!
s.push(item); // 建立一個新元素 壓入棧頂
s.emplace(args);
s.top(); // 返回棧頂元素
- 只能使用介面卡操作! 不能使用底層容器型別的操作
- 棧 預設是基本
deque
實現的,也可以在list
和vector
上實現
佇列介面卡
// queue 和 priority_queue 操作
q.pop(); // 返回queue的首元素 或 priority_queue的最高優先順序元素,但不能刪除此元素
q.front(); // 返回首元素 or 尾元素,但不能刪除此元素
q.back(); // 只適用於 queue!
q.top(); // 返回最高優先順序元素,但不能刪除此元素,只適用於 priority_queue
q.push(item); // 在queue末尾 或 priority_queue中恰當的位置建立一個元素,其值為tiem,或由args構造
q.emplace(args);
queue
預設基於deque
實現. 也可以用list
vector
實現priority_queue
預設基於vector
實現. 也可以用deque
實現priority_queue
允許我們為佇列中的元素建立優先順序.新加入的元素會排列在所有優先順序比它低的已有元素之前!!!