顺序容器----顺序容器概述,容器库概览
一、順序容器概述
一個容器就是一些特定類型對象的集合。順序容器為程序員提供了控制元素存儲和訪問順序的能力。這種順序不依賴于元素的值,而是與元素加入容器時的位置相對應。
順序容器類型:
| 容器類型 | 說明 |
| vector | 可變大小數組。支持快速隨機訪問。在尾部之外的位置插入或刪除元素可能會很慢。 |
| deque | 雙端列表。支持快速隨機訪問。在頭尾位置插入/刪除速度很快。 |
| list | 雙向鏈表。只支持雙向順序訪問。在list中任何位置進行插入/刪除操作速度都很快。 |
| forward_list | 單向鏈表。只支持單向順序訪問。在鏈表任何位置進行插入/刪除操作速度都很快。 |
| array | 固定大小數組。支持快速隨機訪問。不能添加或刪除元素。 |
| string | 與vector相似的容器,但專門用于保存字符。支持快速隨機訪問。在尾部插入/刪除速度快。 |
string和vector將元素保存在連續的內存空間中。由于元素是連續存儲的,由元素的下標來計算其地址是非常快速的。但是,在這兩種容器的中間位置添加或刪除元素就會非常耗時。
list和forward_list這兩個容器的設計目的是令容器任何位置的添加和刪除元素都很快速。作為代價,這兩個容器不支持元素的隨機訪問:為了訪問一個元素,我們只能遍歷整個容器。而且,與vector、deque和array相比,這兩個容器的額外內存開銷也很大。
deque是一個更為復雜的數據結構。與string和vector類似,在deque的中間位置添加或刪除元素的代價(可能)很高。但是,在deque的兩端添加或刪除元素都是很快的(只花費常數時間),與list和forward_list添加刪除元素的速度相當。
forward_list和array是新C++標準增加的類型。與內置數組相比,array是一種更安全、更容易使用的數組類型。forward_list的設計目標是達到與最好的手寫的單向鏈表數據結構相當的性能。因此,forward_list沒有size操作,因為保存或計算其大小就會比手寫鏈表多出額外的開銷。
?
二、容器庫概述
一般來說,每個容器都定義在一個頭文件中,文件名與類型名相同。
順序容器幾乎可以保存任意類型的元素。特別是,我們可以定義一個容器,其元素的類型是另一個容器。這種容器的定義與任何其他容器類型完全一樣。但某些容器操作對元素類型有其自己的特殊要求。我們可以為不支持特定操作需求的類型定義容器,但這種情況下就只能使用那些沒有特殊要求的容器操作了。
所有容器類型(也包括關聯容器)都支持的操作:
| 類型別名 | 說明 |
| iterator | 此容器類型的迭代器類型 |
| const_iterator | 可以讀取元素,但不能修改元素的迭代器類型 |
| size_type | 無符號整數類型,足夠保存此種容器類型最大可能容器的大小 |
| difference_type | 帶符號整數類型,足夠保存兩個迭代器之間的距離 |
| value_type | 元素類型 |
| reference | 元素的左值類型;與value_type&含義相同 |
| const_reference | 元素的const左值類型(即,const value_type&) |
| 構造函數 | ? |
| C c; | 默認構造函數,構造空容器(array不支持) |
| C c1(c2); | 構造c2的拷貝c1 |
| C c(b, e); | 構造c,將迭代器b和e指定的范圍內的元素拷貝到c(array不支持) |
| C c{a, b, c, ...}; | 列表初始化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.clear() | 刪除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.crbegin(), c.crend() | 返回const_reverse_iterator |
| ? | ? |
1、迭代器
標準容器迭代器的運算符:
| 運算符 | 說明 |
| *iter | 返回迭代器iter所指元素的引用 |
| iter->mem | 解引用iter并獲取該元素的名為mem的成員,等價于(*iter).mem |
| ++iter | 令iter指向容器的下一個元素 |
| --iter | 令iter指向容器中的上一個元素 |
| iter1 == iter2 | 判斷兩個迭代器是否相等(不相等),如果兩個迭代器指示的是同一個元素或者它們是同一個容器的尾后迭代器,則相等;反之,不相等 |
| iter1 != iter2 | ? |
其中,forward_list迭代器不支持遞減運算符(--)。
?
string、vector、deque、array的迭代器支持的算術運算:
| 算術運算 | 說明 |
| iter + n | 迭代器加上一個整數值仍得一個迭代器,迭代器指示的新位置與原來相比向前移動了若干個元素。結果迭代器或者指示容器內的一個元素,或者等于容器尾后迭代器 |
| iter - n | 迭代器減去一個整數值仍得一個迭代器,迭代器指示的新位置與原來相比向后移動了若干個元素 |
| iter += n | 迭代器加法的復合賦值語句,將iter加n的結果賦給iter |
| iter -= n | 迭代器減法的復合賦值語句,將iter減n的結果賦給iter |
| iter1 - iter2 | 兩個迭代器相減的結果是它們之間的距離。參與運算的必須是同一個容器的迭代器 |
| >、>=、<、<= | 迭代器的關系運算符,如果某迭代器指向的容器位置在另一個迭代器所指位置之前,則說明前者小于后者。參與運算的必須是同一個容器的迭代器 |
注意:上面的迭代器的范圍區間是[begin(),end()],如果運算結果不在這個區間內,會報錯。
一個迭代器范圍是由一對迭代器表示,兩個迭代器分別指向同一個容器中的元素或者尾元素之后的位置。這兩個迭代器通常被稱為begin和end。這種元素范圍被稱為左閉右合區間,其標準數學描述為:[begin, end)。
當auto與begin或end結合使用時,獲得的迭代器類型依賴于容器類型,如果對象是常量,返回const_iterator;如果對象不是常量,返回iterator。cbegin和cend不論對象是否是常量,返回值都是const_iterator。
?
2、容器定義和初始化
| 操作 | 說明 |
| C c | 默認構造函數。如果c是一個array,則c中元素按默認方式初始化;否則c為空 |
| C c1(c2) | c1初始化為c2的拷貝。c1和c2必須是相同類型(即,它們必須是相同的容器類型,且保存的是相同的元素類型;對于array來說,兩者還必須具有相同大小) |
| C c1=c2 | 同上 |
| C c{a, b, c, ...} | c初始化為初始化列表中元素的拷貝。列表中元素的類型必須與c的元素類型相容。對于array類型,列表中元素數目必須等于或小于array的大小,任何遺漏的元素都進行值初始化。初始化列表隱含地指定了容器的大小:容器將包含與初始值一樣多的元素。 |
| C c={a, b, c, ...} | 同上 |
| C c(b, e) | c初始化為迭代器b和e指定范圍中的元素的拷貝。范圍中元素的類型必須與c的元素類型相容(array不適用) |
| 只有順序容器(不包括array)的構造函數才能接受大小參數 | ? |
| C seq(n) | seq包含n個元素,這些元素進行了值初始化;此構造函數是explicit的 |
| C seq(n, t) | seq包含n個初始化為值t的元素 |
將一個容器初始化為另一個容器的拷貝的例子:
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <deque> 5 #include <list> 6 #include <forward_list> 7 #include <array> 8 9 int main() 10 { 11 std::list<std::string> authors = { "abc", "QAQ", "hello" }; 12 std::vector<const char*> articles = { "a", "ab", "the" }; 13 14 std::list<std::string> list2(authors); // 類型匹配 15 //std::deque<std::string> auth(authors); // 錯誤:容器類型不匹配 16 std::forward_list<std::string> words(articles.begin(), articles.end()); // 元素類型可以相互轉換 17 return 0; 18 } View Code1)標準庫array具有固定大小
與內置數組一樣,標準庫array的大小也是類型的一部分。當定義一個array時,除了指定元素類型,還要指定容器大小。使用array時也要指定容器大小。
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <deque> 5 #include <list> 6 #include <forward_list> 7 #include <array> 8 9 int main() 10 { 11 std::array<int, 10> arr; // 類型為10個int的數組 12 std::array<int, 10>::size_type i; // 數組類型包括元素類型和大小 13 return 0; 14 } View Code一個默認構造的array是非空的:它包含了與其大小一樣多的元素。這些元素都被默認初始化。如果我們對array進行列表初始化,初始值的數目必須等于或小于array的大小。如果初始值數目小于array的大小,則它們被用來初始化array中靠前的元素,所有剩下的元素都會進行值初始化。
雖然我們不能對內置數組類型進行拷貝或對象賦值操作,但array并無此限制。
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <deque> 5 #include <list> 6 #include <forward_list> 7 #include <array> 8 9 int main() 10 { 11 std::array<int, 10> arr = { 2, 3 }; 12 std::array<int, 10> copy = arr; 13 return 0; 14 } View Code?
3、賦值和swap
? 賦值運算符將其左邊容器中的全部元素替換為右邊容器中元素的拷貝。
所有容器都支持的賦值運算:
| 操作 | 說明 |
| c1=c2 | 將c1中的元素替換為c2中元素的拷貝。c1和c2必須具有相同的容器類型,且保存的是相同的元素類型 |
| c={a, b, c, ...} | 將c1中的元素替換為初始化列表中元素的拷貝(array不適用)。保存的必須是相同的元素類型 |
| swap(c1, c2) | 交換c1和c2中的元素。c1和c2必須具有相同的類型。swap通常比從c2向c1拷貝元素快得多 |
| c1.swap(c2) | 同上 |
| assign操作不適用于關聯容器和array | ? |
| seq.assign(b, e) | 將seq中的元素替換為迭代器b和e所表示的范圍中的元素。迭代器b和e不能指向seq中的元素。保存的元素類型必須相容。 |
| seq.assign(items) | 將seq中的元素替換為初始化列表items中的元素 |
| seq.assign(n, t) | 將seq中的元素替換為n個值為t的元素? |
賦值相關運算會導致指向左邊容器內部的迭代器、引用和指針失效。
swap操作將容器內容交換不會導致指向容器的迭代器、引用和指針失效(容器類型為array和string的情況除外)。
1)使用assign(僅順序容器)
順序容器(array除外)還定義了一個名為assign的成員,允許我們從一個不同但相容的類型賦值,或者從容器的一個子序列賦值。
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <deque> 5 #include <list> 6 #include <forward_list> 7 #include <array> 8 9 int main() 10 { 11 std::vector<int> vec = { 1, 2, 3 }; 12 std::vector<double> vec2 = { 4.5, 5.5, 6.5 }; 13 // vec = vec2; // 錯誤:類型不同 14 std::list<double> lst = { 7, 8, 9 }; 15 vec.assign(lst.begin(), lst.end()); 16 return 0; 17 } View Code2)使用swap
除array外,交換兩個容器內容的操作保證會很快----元素本身并未交換,swap只是交換了兩個容器的內部數據結構。
元素不會被移動的事實意味著,除string外,指向容器的迭代器、引用和指針在swap操作都不會失效。他們仍然指向操作之前所指向的那些元素。但是,在swap之后,這些元素已經屬于不同的容器了。
對一個string調用swap會導致迭代器、引用和指針失效。
swap兩個array會真正交換它們的元素。因此,交換兩個array所需的時間與array中元素的數目成正比。
因此,對于array,在swap操作之后,指針、引用和迭代器所綁定的元素保持不變,但元素值已經與另一個array中對應元素的值進行了交換。
?
5、關系運算符
每個容器類型都支持相等運算符(==和!=);除了無序關聯容器外的所有容器都支持關系運算符(>、>=、<、<=)。關系運算符左右兩邊的運算對象必須是相同類型的容器,且必須保存相同類型的元素。
比較兩個容器實際上是進行元素的比較。這些運算符的工作方式與string的關系運算類似:
a、如果兩個容器具有相同大小且所有元素都兩兩對應相等,則這兩個容器相等;否則兩個容器不等。
b、如果兩個容器大小不同,但較小容器中每個元素都等于較大容器中的對應元素,則較小容器小于較大容器。
c、如果兩個容器都不是另一個容器的前綴子序列,則它們的比較結果取決于第一個不相等的元素的比較結果。
只有當其元素類型也定義了相應的比較運算符時,我們才可以使用關系運算符比較兩個容器。
容器的相等運算實際上是使用元素的==運算符實現比較的,而其他關系運算符是使用元素的<運算符。如果元素不支持所需運算符,那么保存這種元素的容器就不能使用相應的關系運算。
轉載于:https://www.cnblogs.com/ACGame/p/10219585.html
總結
以上是生活随笔為你收集整理的顺序容器----顺序容器概述,容器库概览的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python315题的漫漫通关之路
- 下一篇: 乘风破浪:LeetCode真题_038_