C++ 筆記:順序容器
阿新 • • 發佈:2018-12-13
順序容器概述
- 順序容器型別:
vector // 可變大小陣列;快速隨機訪問;尾部插入刪除
deque // 雙端佇列;快速隨機訪問;頭尾插入刪除
list // 雙向連結串列;雙向順序訪問;任意位置插入刪除
forward_list // 單向連結串列;單向順序訪問;任意位置插入刪除
array // 陣列;快速隨機訪問;不能插入刪除
string // 可變長陣列;快速隨機訪問;尾部插入刪除
- 選用哪一種容器:
- 通常首選
vector
- 要求隨機訪問元素, 則使用
vector
或deque
- 要求在容器的中間插入或刪除元素,則使用
list
forward_list
- 只要求在頭尾插入或刪除,則使用
deque
- 如果程式只有在讀取輸入時才需要在容器中間位置插入元素, 隨後需要隨機訪問元素,則
- 處理輸入資料時,通常可以先向
vector
追加資料,然後再呼叫標準庫的sprt
重排容器中的元素,從而避免在中間位置新增元素 - 如果必須在中間位置插入元素,考慮在輸入階段使用
list
,輸入完成後則將list
中的內容拷貝到vector
中 - 既需要隨機訪問元素,又需要在容器中間位置插入元素,則需要對比測試兩種容器的效能
- 處理輸入資料時,通常可以先向
- 通常首選
迭代器
- 所有標準庫容器都可以使用迭代器
- 可以使用迭代器的型別擁有返回迭代器的成員函式
vector< T> vec{t1, t2, ..., tn};
auto b = vec.begin(), e = vec.end(); // b 指向 vec 的第一個元素,e 指向 vec 的尾後元素
// 一般情況下不必關心迭代器本身的型別,因此此處使用 auto
- 空容器的
begin()
和end()
返回的都是尾後迭代器返回的都是尾後迭代器 - 使用迭代器時不允許改變容器的大小
迭代器運算
*iter // 解引用,返回 iter 所指元素的引用
iter->member // 返回 iter 所指元素中的 member 成員,相當於 (*iter).member
++iter, --iter // 移動 iter,forward_list 不支援迭代器的遞減運算
// 以下只適用於 string、vector、deque 和 array
iter + n, iter - n
iter += n, iter -= n
iter1 == iter2, iter1 != iter2
iter1 - iter2
iter1 <, <=, >=, > iter2
- 使用迭代器遍歷處理容器:
vector<int> ivec{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (auto iter = ivec.begin(); iter != ivec.end(); ++iter)
*iter *= *iter;
for (auto i : ivec)
cout << i << " ";
cout << endl; // output: 0 1 4 9 16 25 36 49 64 81
string text = "hello, world!";
for (auto iter = text.begin(); iter != text.end(); ++iter)
*iter = toupper(*iter);
for (auto s : text)
cout << s;
cout << endl; // output: HELLO, WORLD!
- 使用迭代器實現二分搜尋:
template <typename T1, typename T2>
size_t binarySearch(const T1 & vec, const T2 & target) {
auto begin = vec.cbegin(), end = vec.cend();
auto mid = begin + (end - begin) / 2;
while (mid != end && *mid != target) {
if (target < *mid)
end = mid;
else
begin = mid + 1;
mid = begin + (end - begin) / 2;
}
if (mid == vec.cend()) // 必須先判斷 target 是否大於 vec 中的所有元素
return mid - vec.cbegin();
else if (*mid == target)
return mid - vec.cbegin();
else
return vec.cend() - vec.cbegin();
}
所有容器都提供的操作
- 所有容器都定義在同名的標頭檔案中
- 容器均為模板類,必須提供額外的型別資訊來生成具體的容器型別
- 容器操作:
// 型別成員
Container::iterator, Container::const_iterator
Container::reverse_iterator, Container::const_reverse_iterator // 反向迭代器型別
Container::size_type, Container::different_type
Container::value_type, Container::reference, Container::const_reference
// 建構函式
Container c; // 構造空容器
Container c2(c1), c4 = c3;
Container c1{ele1, ele2, ...}, c2 = {ele1, ele2, ...};
Container c(begin, end); // 將迭代器 begin 和 end 指定範圍內的元素拷貝到 c,不包括 end 指向
// 的元素,不支援 array
// 賦值與 swap
c1 = c2;
c1 = {ele1, ele2, ...};
c1.swap(c2);
swap(c1, c2);
// 大小與比較
c.size()
c.max_size()
c.empty()
c1 ==, != c2
c1 <, <=, >=, > c2
// 迭代器
c.begin(), c.end()
c.cbegin(), c.cend()
c.rbegin(), c.rend() // 反向迭代器
c.crbegin(), c.crend()
// 插入刪除,不支援 array
c.insert()
c.emplace()
c.erase()
c.clear()
- 順序容器還支援以下建構函式:
Container c(n) // c 包含 n 個值初始化了的元素,不支援 string 和 array,且元素應是
// 內建型別或具有預設建構函式
Container c(n, ele) // c 包含 n 個值為 ele 的元素,不支援 array
標準庫型別 array
- 使用
array
型別必須同時包括元素型別和容器大小:
array<int, 10> = int_arr;
array<string, 10> = str_arr;
array<int, 10>::size_type t1;
array<string, 10>::difference_type t2;
- 只要型別匹配(元素型別和容器大小),可以對
array
物件進行拷貝和賦值
assign()
assign()
允許從一個不同但相容的型別賦值,或從容器的子序列賦值
seq.assign(begin, end);
seq.assign(ini_list);
seq.assign(n, ele);
assign()
用引數所指定的元素的拷貝替換左邊容器中的所有元素
swap()
swap()
要求兩個容器型別相同- 除
array
外,swap()
不對任何元素進行拷貝、刪除或插入操作 swap()
兩個array
則會真正交換它們的元素- 建議使用非成員函式版本的
swap()
順序容器的操作
向順序容器新增元素
- 不支援
array
forward_list
有自己的insert()
和emplace()
版本,且不支援push_back()
和emplace_back()
操作vector
和string
不支援push_front()
和emplace_front()
操作
push_back()
:追加一個元素到容器尾部
- 不支援
array
和forward_list
string word;
while (cin >> word)
container.push_back(word);
push_front()
:將一個元素插入到容器頭部
- 不支援
array
、vector
和string
list<int> ilist;
for (int i = 0; i < 10; ++i)
ilist.push_front(i);
insert()
:將元素插入到迭代器所指定的位置之前
- 不支援
array
forward_list
有自己的insert
版本- 常用於
list
insert()
總是接受一個迭代器作為其第一個引數,並將元素插入迭代器指定的位置之前- 在指定位置插入一個元素:
list<string> str_lst{"Hello", "world!"};
auto iter = str_lst.begin() + 1;
str_lst.insert(iter, ", ");
- 在指定位置插入多個相同的元素:
list<string> str_lst{"Hello", "world!"};
auto iter = str_lst.begin();
str_lst.insert(iter, 10, "Alice");
- 在指定位置插入一個元素列表:
list<string> str_lst{"Hello", "world!"};
auto iter = str_lst.end();
str_lst.insert(iter, {"these", "words", "go", "at", "the", "end"});
- 在指定位置插入一個範圍內的元素:
list<string> str_lst{"Hello", "Alice"};
vector<string> str_vec{"Welcome", "to", "the", "C++", "world"};
auto iter = str_lst.end();
str_lst.insert(iter, str_vec.begin(), str_vec.end() - 1);
insert()
返回指向第一個新加入元素的迭代器,因此可以在指定位置反覆呼叫insert()
插入元素
emplace_front(args)
、emplace(args)
、emplace_back(args)
- 在容器頭部、指定位置、尾部使用引數直接構造元素(而非拷貝)
- 傳遞給
emplace()
函式的引數args
必須與元素型別的建構函式相匹配
訪問容器中的元素
front()
, back()
:返回容器首、尾元素的引用
back()
不支援forward_list
- 呼叫
front()
和back()
之前應檢查容器中是否確有元素
下標 [n]
和 at(n)
- 不支援
list
和forward_list
- 返回的同樣是元素的引用
刪除容器中的元素
- 不支援
array
forward_list
有自己的erase()
版本forward_list
不支援pop_back()
vector
和string
不支援pop_front()
- 不能對空容器執行刪除元素的操作,因此必須先檢查容器是否為空
pop_front()
、pop_back()
:刪除容器的首、尾元素
- 返回
void
,若需要被刪除的元素,則應先儲存再刪除
erase()
:刪除指定位置或指定範圍的元素
- 返回指向最後一個刪除的元素之後位置的迭代器
clear()
:刪除容器中的所有元素
- 返回
void
改變容器的大小 resize()
- 不支援
array
- 如果當前大小大於所要求的大小,容器後部的元素會被刪除;如果當前大小小於新大小,會將新元素新增到容器後部
list<int> ilist(10, 1024);
ilist.resize(15); // 將 5 個值為 0 的元素新增到 ilist 的末尾
ilist.resize(25, -1); // 將 10 個值為 -1 的元素新增到 ilist 的末尾
ilist.resize(5); // 從 ilist 末尾刪除 20 個元素