1. 程式人生 > >C++ 筆記:順序容器

C++ 筆記:順序容器

順序容器概述

  • 順序容器型別:
vector          // 可變大小陣列;快速隨機訪問;尾部插入刪除
deque           // 雙端佇列;快速隨機訪問;頭尾插入刪除
list            // 雙向連結串列;雙向順序訪問;任意位置插入刪除
forward_list    // 單向連結串列;單向順序訪問;任意位置插入刪除
array           // 陣列;快速隨機訪問;不能插入刪除
string          // 可變長陣列;快速隨機訪問;尾部插入刪除
  • 選用哪一種容器:
    • 通常首選 vector
    • 要求隨機訪問元素, 則使用 vectordeque
    • 要求在容器的中間插入或刪除元素,則使用 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() 操作
  • vectorstring 不支援 push_front()emplace_front() 操作

push_back():追加一個元素到容器尾部

  • 不支援 arrayforward_list
string word;
while (cin >> word)
  container.push_back(word);

push_front():將一個元素插入到容器頭部

  • 不支援 arrayvectorstring
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)

  • 不支援 listforward_list
  • 返回的同樣是元素的引用

刪除容器中的元素

  • 不支援 array
  • forward_list 有自己的 erase() 版本
  • forward_list 不支援 pop_back()
  • vectorstring 不支援 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 個元素