STL基礎--容器
阿新 • • 發佈:2018-12-30
容器種類
- 序列容器(陣列,連結串列)
- Vector, deque, list, forward list, array
- 關聯容器(二叉樹),總是有序的
- set, multiset根據值排序,元素值不能修改
- map, multimap根據key排序,鍵值不能修改
- 無序容器(hash 表)
- 無序set/multiset
- 無序map/multimap
序列容器
Vector
vector<int> vec; // vec.size() == 0 vec.push_back(4); vec.push_back(1); vec.push_back(8); // vec: {4, 1, 8}; vec.size() == 3 // Vector特定的操作: cout << vec[2]; // 8 (沒有範圍檢查) cout << vec.at(2); // 8 (超出範圍會拋range_error exception異常) for (int i; i < vec.size(); i++) { cout << vec[i] << " "; for (list<int>::iterator itr = vec.begin(); itr!= vec.end(); ++itr) cout << *itr << " "; for (it: vec) // C++ 11 cout << it << " "; // Vector是記憶體中一個動態分配的連續的陣列 int* p = &vec[0]; p[2] = 6; // 所有容器共有的一些成員函式 // vec: {4, 1, 8} if (vec.empty()) { cout << "Not possible.\n"; } cout << vec.size(); // 3 vector<int> vec2(vec); // 拷貝構造, vec2: {4, 1, 8} vec.clear(); // 刪除所有元素; vec.size() == 0 vec2.swap(vec); // 交換,vec2變成空的,vec有3個元素 // 注:沒有抽象帶來的開銷,非常高效 /* Vector的特性: * 1. 在末尾插入/刪除很快: O(1) * 2. 在其他地方插入/刪除s慢: O(n) * 3. 搜尋慢: O(n) */
Deque
deque<int> deq = { 4, 6, 7 };
deq.push_front(2); // deq: {2, 4, 6, 7}
deq.push_back(3); // deq: {2, 4, 6, 7, 3}
// Deque介面跟vector類似
cout << deq[1]; // 4
/* 特性:
* 1. 在首尾插入/刪除快
* 2. 中間插入/刪除慢: O(n)
* 3. 搜尋慢: O(n)
*/
List
* -- 雙向連結串列 * 元素儲存在記憶體不同的地方,cache會經常miss。很多STL實現會將其元素放到一起,但即使如此因為包含了太多指標,會消耗更多的記憶體(意味著更多的cache miss和page fault) */ list<int> mylist = {5, 2, 9 }; mylist.push_back(6); // mylist: { 5, 2, 9, 6} mylist.push_front(4); // mylist: { 4, 5, 2, 9, 6} list<int>::iterator itr = find(mylist.begin(), mylist.end(), 2); // itr -> 2 mylist.insert(itr, 8); // mylist: {4, 5, 8, 2, 9, 6} // O(1), 比vector/deque更快 itr++; // itr -> 9 mylist.erase(itr); // mylist: {4, 8, 5, 2, 6} O(1) /* 特性: * 1. 任何地方插入/刪除快: O(1) * 2. 搜尋慢: O(n) * 3. 沒有隨機訪問,即沒有 [] 運算子 */ // 拼接速度快 mylist1.splice(itr, mylist2, itr_a, itr_b ); // O(1)
Array
// 不能改變大小,不能元素個數的Array是不同型別
int a[3] = {3, 4, 5};
array<int, 3> a = {3, 4, 5};
a.begin();
a.end();
a.size();
a.swap();
array<int, 4> b = {3, 4, 5};
關聯容器
- 基於二叉樹,總是排序的,預設基於 < 排序
- 沒有push_back(), push_front()
set和multiset
// 元素不能重複 set<int> myset; myset.insert(3); // myset: {3} myset.insert(1); // myset: {1, 3} myset.insert(7); // myset: {1, 3, 7}, O(log(n)) set<int>::iterator it; it = myset.find(7); // O(log(n)), it指向7 // 序列容器沒有find函式 pair<set<int>::iterator, bool> ret; ret = myset.insert(3); // 不插入新元素 if (ret.second==false) it=ret.first; // "it"當前指向元素3 myset.insert(it, 9); // myset: {1, 3, 7, 9} O(log(n)) => O(1),it作為插入元素位置的一個提示,並不是實際插入位置 // it 指向3 myset.erase(it); // myset: {1, 7, 9} myset.erase(7); // myset: {1, 9} // 注:序列容器沒有這種根據元素值刪除元素的erase // multiset允許元素重複的set multiset<int> myset; // set/multiset: 元素值都是不能修改的 *it = 10; // *it只讀 /* 特性: * 1. 搜尋快: O(log(n)) * 2. 因為也是通過指標聯絡(cache miss和page fault的問題),遍歷慢 (相比vector & deque) * 3. 沒有隨機訪問,即沒有 [] 運算子 */
map和multimap
/*
* map
* - key值不能重複
*/
map<char,int> mymap;
mymap.insert ( pair<char,int>('a',100) );
mymap.insert ( make_pair('z',200) );
map<char,int>::iterator it = mymap.begin();
mymap.insert(it, pair<char,int>('b',300)); // "it"只是一個提示
it = mymap.find('z'); // O(log(n))
// showing contents:
for ( it=mymap.begin() ; it != mymap.end(); it++ )
cout << (*it).first << " => " << (*it).second << endl;
// multimap允許鍵值重複
multimap<char,int> mymap;
// map/multimap:
// -- 鍵值不能修改
// type of *it: pair<const char, int>
(*it).first = 'd'; // Error
// 為何叫“關聯”容器
// 因為value和key是關聯的,set是特殊的map,value等於key
無序容器(C++ 11)
- 無序的set/multiset
- 無序的map/multimap
- 順序是未定義的,可能隨時間改變
/*
* 基本型別和string都定義了預設的hash函式
*
* 沒有下標操作[]和at()
* 沒有push_back()和push_front()
*/
unordered_set<string> myset = { "red","green","blue" };
unordered_set<string>::const_iterator itr = myset.find ("green"); // O(1)
if (itr != myset.end()) // 重要的檢查
cout << *itr << endl;
myset.insert("yellow"); // O(1)
vector<string> vec = {"purple", "pink"};
myset.insert(vec.begin(), vec.end());
// Hash表特定的APIs:
cout << "load_factor = " << myset.load_factor() << endl; // 負載因子:總元素和總桶數的比值
string x = "red";
cout << x << " is in bucket #" << myset.bucket(x) << endl; // x位於哪個桶
cout << "Total bucket #" << myset.bucket_count() << endl; // 總的桶數
// 無序multiset,無序map和multimap也是類似
// hash衝撞 => 效能退化
/* 無序容器的特性:
* 1. 在任何位置搜尋/插入都最快: O(1)
* 關聯容器O(log(n))
* vector, deque O(n)
* list插入O(1) , 搜尋O(n)
* 2. Unorderd set/multiset: 元素值不能改變
* Unorderd map/multimap: key不能改變
*/
Associative Array
/*
* 一般指的是map和unordered map
*/
unordered_map<char, string> day = {{'S',"Sunday"}, {'M',"Monday"}};
cout << day['S'] << endl; // 沒有範圍檢查
cout << day.at('S') << endl; // 有範圍檢查
vector<int> vec = {1, 2, 3};
vec[5] = 5; // Compile Error
day['W'] = "Wednesday"; // Inserting {'W', "Wednesday}
day.insert(make_pair('F', "Friday")); // Inserting {'F', "Friday"}
day.insert(make_pair('M', "MONDAY")); // key已經存在,插入失敗
day['M'] = "MONDAY"; // 可以通過下標修改值
void foo(const unordered_map<char, string>& m) {
//m['S'] = "SUNDAY";
//cout << m['S'] << endl; //編譯出錯,下標操作有寫許可權,編譯器看到以為要對const變數進行寫操作
auto itr = m.find('S'); //使用find替代
if (itr != m.end())
cout << *itr << endl;
}
foo(day);
//關於Associative Array:
//1. 搜尋時間: unordered_map, O(1); map, O(log(n));
//2. 但是Unordered_map可能退化為O(n);
//3. 不能使用multimap和unordered_multimap, 它們的key不唯一,也沒有下標操作[]
容器介面卡
- 為了滿足某種特殊需要,提供受限制的介面
- 通過基本的容器類實現
*
* 1. 棧 stack: LIFO, push(), pop(), top()
*
* 2. 佇列 queue: FIFO, push(), pop(), front(), back()
*
* 3. 優先順序佇列 priority queue: 第一個元素總是具有最高優先順序
* push(), pop(), top()
*/
另一種容器分類的方法
*
* 1. 基於陣列的容器: vector, deque
*
* 2. 基於結點的容器: list + associative containers + unordered containers
*
* 基於陣列的容器會使指標失效:
* - 原生指標,迭代器,引用
*/
vector<int> vec = {1,2,3,4};
int* p = &vec[2]; // p points to 3
vec.insert(vec.begin(), 0);
cout << *p << endl; // 2, or ?
// 運氣好,列印2;運氣不好,未定義