STL六大元件 之 容器 和 容器介面卡
一、STL容器:
- 順序容器:
(1)vector 向量容器,底層是由陣列實現的。
初始化預設記憶體容量是0,有第一個元素時開闢一個元素大小,接下來的擴容以2倍的大小自動增長。(VS下是1.5倍)
vector也可以在定義時直接指定空間大小和初始值:vector<type> iv(2,9);
vetor擴充空間包括三個步驟:重新配置記憶體,移動資料,釋放原空間。所以一旦進行空間重新配置,使用者自己定義的指向原vector的所有迭代器就都失效了,而vector自帶的迭代器會被調整,指向新的vector。
重新配置空間時,先確定空間大小,再通過空間配置函式去配置空間
常用操作方法:begin() end()
size() 返回元素個數
capacity() 返回空間總大小
push_back(val) 插入一個元素
insert(it,n,val) 在it迭代器前插入n個val
pop_pack() 從最後刪除一個元素
erase(it) 刪除it迭代器指向的元素 該操作會使it迭代器失效,但會返回it下一個元素的迭代器
clear() 清除所有元素
erase(first,last) 刪除first和last之間的所有元素
(2)list 雙向連結串列容器,底層由雙向連結串列實現(含尾節點(為了保證迭代器的通用性))
SGI list 是一個環狀雙向迴圈連結串列,所以只需要一個指標便可以表現整個連結串列。
迭代器失效:list的插入(insert)和接合(splice)都不會造成迭代器的失效。
元素刪除(erase)只有指向被刪除元素的迭代器才會失效。
常用操作方法:begin() end()
size() 返回當前節點個數(元素)
push_back(val) 後插
push_front(val) 前插
pop_back() 後刪
pop_front() 前刪
insert(it,val) 在it迭代器之前插入值為val的節點
erase(it) 刪除it迭代器指向的節點
clear() 清除所有節點(整個連結串列)
remove(val) 將val之前的所有元素移除
unique() 移除連結串列中相同的連續元素,相同的只剩餘一個
splice(it,list1) 將list1整個連結串列結合於it之前,不能是同一個list
splice(it,list, it1) 將i所指元素結合於it之前,可以是同一個list
splice(it,list,first,last) 將(first,last)內的所有元素接合到it之前
merge(list1) 將list1 合併到 *this上,但兩個list都必須是遞增序列
reverse() 將*this的內容逆向重置
sort() 對元素排序,list自身的成員函式。因為通用演算法sort只接受隨機迭代器。
(3)deque 雙端佇列容器,底層用雙端佇列實現 【可以下標操作】
緩衝區:實際存放資料的記憶體空間。大小預設為512bytes/sizeof(T),可以由使用者自行設定,
map:中控器 ,管理的節點數:最小為8,最多是 所需要節點數加2(所需節點數:(元素個數/每個緩衝區可容納的元素個數) +1 )。
當所有map結點都使用完了,一樣要進行空間的重新配置(配置更大的,拷貝原來的,釋放原來的)。
例:元素形態為int,緩衝區去大小為8的deque(指定了的deque<int,alloc,8>)。假設deque有20 個數據。所以就需要20/8 = 3個緩衝區,所以map使用了3個節點。
資料結構如下:(空白是申請的空間,陰影部分為備用空間)
常用操作方法:pop_back() pop_front() clear() erase(it) erase(first,last)
push_back(val) push_front(val) size() insert(it,val)
三者的比較:
vector和deque:①deque允許常數時間內對起頭端進行元素的插入和移除。vector的頭部插入和刪除效率是十分低的
②deque沒有容量的概念。因為deque是動態的以分段連續空間組合而成的,隨時可以增加一段新的空間並連線起來,不會因為記憶體不足而重新配置空間【一定情況下也需要重新配置空間:當map使用率已經滿載,便需要一塊更大的空間作為map,但相比vector的重新配置空間,deque對map的重新配置要比vector要代價小】
③deque的迭代器不是普通的指標,其複雜度遠遠大於vector,所以在運算層面上來說,vector要比deque的效率要高的多。
vector和list:list相對於vector和deque來說,對於任意位置的元素插入和刪除效率要遠遠大於其他兩個。
- 關聯容器:
底層使用紅黑樹(平衡樹,排序樹,【資料有序】,add、delete、query(查詢)
時間複雜度都是O(log2n))
【所有元素會根據元素的鍵值自動被排序。】
(1)set 單重集合
multiset 多重集合
性質:元素值就是鍵值。
set不允許有兩個相同的元素/鍵值,multiset允許有相同的元素/鍵值。
不可以通過迭代器改變set的元素值,因為set的元素值就是鍵值,改變鍵值會破壞set組織
元素刪除後只有指向被刪除元素的迭代器會失效。
常用操作方法:
增:insert(val); 時間複雜度: O(log2n)
刪:erase(val); 時間複雜度: O(log2n)
查:find(key);<--set的成員方法【原因與map一致】 時間複雜度: O(log2n)
另一個重要方法:count(val);統計該值存在的個數,但在單重集合中不允許關鍵字重複
(2)map:對映表<key,value> 底層也能用雜湊表實現。根據key排序
multimap :多重集合,允許key重複
性質:同時擁有鍵值(key)和實值(value)。第一個元素是鍵值,第二個元素是實值。
map不允許有兩個相同的鍵值。multimap允許有相同的鍵值。
不可以通過迭代器修改map的鍵值,但可以修改實值。
元素刪除後只有指向被刪除元素的迭代器會失效。
常用操作方法:
增:insert(make_pair(key,value)); O(log2n)
map[key] = value; 也可以使用下標的方式進行插入
刪:erase(key); O(log2n)
查:find(key);<---map的成員方法 O(log2n)
【map的成員方法是根據map的底層結構來進行實現的,效率自然比全域性的泛型演算法高】
【順序容器沒有find的成員方法,因而只能用全域性的find演算法】
STL還提供了一個通過val查詢的方法:find_if(first,last,pred);pred是用於比較數值的函式或者函式物件。
(3)hashtable:散列表 底層資料結構:以vector為buckets,以開鏈處理雜湊碰撞。
如圖:
hashtable:bucktes的個數以28個質數為底。如:使用者定義一個n為50的hashtable,得到的散列表實際桶數量為53(第一個質數),這裡直接用vector下的空間申請函式reserve去處理,並用<vector>insert將恐案件初始化為0
對於插入元素操作,有兩個函式:①insert_unique,不允許key重複,②insert_equal,允許key重複。
1)兩個插入在開始時都要先判斷是否需要重新建表。這裡呼叫resize<hashtable>函式,原則是:當元素個數(加上新增元素)大於“桶”的個數時,就重建表格。
重建表格當然首先要重新申請新的空間<大小為下一個質數>,將原buckets臉上的元素拷貝到新表中,然後新舊buckets對調,最後將舊錶釋放。
2)插入新元素時,先要對新元素進行hash。這裡講hash function進行包裝,以_bkt_num函式為介面,該函式去呼叫bkt_bun_key函式,在bkt_num_key中將hash function得到的值進行取模運算。
所以hash function只需將原值返回即可,對char*型別字串則要進行特殊處理。
hash function是仿函式,過載operator()運算子,在該過載函式中進行值的返回即可。但也有部分型別未被過載,所以不可處理。
(4)hash_set、hash_map、hash_multiset、hash_multimap :
這四種關聯式容器,底層機制都是hashtable,元素不會被自動排序。
除此之外他們與原身(set、map、multiset、multimap)的特性完全相同。當然hash_table不能處理的型別,他們也無法處理。
二、容器介面卡:
1、stack:棧 底層預設使用deque的資料結構,將一端封閉,實現後進先出的行為。
只能在頂端進行元素的插入、刪除、取得(get),所以不允許進行遍歷,沒有迭代器。
使用者也可以自己指定底層的資料結構,如:stack<int,list<int> >。這個stack底層使用的額資料結構就是list。因為list也是雙向開口的資料結構,所需的成員函式list也都有,所以以list為底部結構並封閉其埠開口,實現stack的先進先出特性。
常用操作方法:
top 【返回棧頂元素】
push 【入棧,插到棧頂】
pop 【棧頂元素出棧】
size 【返回棧中元素的個數】
empty 【判斷棧是否為空】
2、queue:佇列 底層預設使用deque的資料結構,封閉低端的出口和頂端的入口,實現先進先出(從底端入,從頂端出)的性質。
因為只能從底端入,從頂端出,所以也不允許遍歷行為。沒有迭代器。
同理,使用者也可以自己指定queue的底層資料結構:queue<int ,list<int> >。用list作為queue的底層容器。
常用操作方法:
push 【入隊插到隊尾】
pop 【隊首元素出隊】
size 【返回佇列中元素的個數】
front 【返回佇列中第一個元素】
back 【返回佇列中最後一個元素】
empty 【判斷佇列是否為空】
3、qriority_queue:帶權佇列 即queue中的元素不是按照被推入的次序排列,而是按照元素的權值(實值)排列,權值最高的,排在最前面。
預設情況下qriority_queue底層使用max_heap(以vector)實現,可以滿足以“權值高低自動排序”的特性。
與queue一樣,qriority_queue不提供遍歷的功能,也不提供迭代器。