天天看點

全網最全面的STL總結與常見面試題,值得收藏(三)

priority_queue

優先隊列,其底層是用堆來實作的。在優先隊列中,隊首元素一定是目前隊列中優先級最高的那一個。

set

set 是按照特定順序存儲唯一進制素的容器。

template<class _Kty,
    class _Pr = less<_Kty>,
    class _Alloc = allocator<_Kty> >
class set      

set 的 底層資料結構是 紅黑樹,一種高效的平衡檢索二叉樹。

set 容器中 每一個元素就是二叉樹的每一個節點,對于set容器的插入删除操作,效率都比較高,原因是因為二叉樹的删除插入元素并不需要進行記憶體拷貝和記憶體移動,隻是改變了指針的指向。

對 set 進行插入删除操作 都不會引起iterator的失效,因為疊代器相當于一個指針指向每一個二叉樹的節點,對set的插入删除并不會改變原有記憶體中節點的改變, 但是vector的插入删除操作一般會發生記憶體移動和記憶體拷貝,是以會發生疊代器的失效。

set容器的檢索速度很快,因為采用二分查找的方法 。

multiset

multiset允許元素重複而set不允許。

template<class _Kty,
    class _Pr = less<_Kty>,
    class _Alloc = allocator<_Kty> >
class multiset      

map

map 是關聯容器,按照特定順序存儲由 key value (鍵值) 和 mapped value (映射值) 組合形成的元素。

由于 RB-tree 是一種平衡二叉搜尋樹,自動排序的效果很不錯,是以标準的STL map 即以 RB-tree 為底層機制。又由于 map 所開放的各種操作接口,RB-tree 也都提供了,是以幾乎所有的 map 操作行為,都隻是轉調 RB-tree 的操作行為。

方法 說明

map 構造函數

begin 傳回引用容器中第一個元素的疊代器

key_comp 傳回容器用于比較鍵的比較對象的副本

value_comp 傳回可用于比較兩個元素的比較對象,以擷取第一個元素的鍵是否在第二個元素之前

find 在容器中搜尋具有等于 k的鍵的元素,如果找到傳回一個疊代器,否則傳回 map::end

count 在容器中搜尋具有等于 k(參數)的鍵的元素,并傳回比對的數量

lower_bound 傳回一個非遞減序列 [first, last)中的第一個大于等于值 val的位置的疊代器

upper_bound 傳回一個非遞減序列 [first, last)中第一個大于 val的位置的疊代器

equal_range 擷取相同元素的範圍,傳回包含容器中所有具有與 k等價的鍵的元素的範圍邊界

multimap

multimap 的特性以及用法與 map 完全相同,唯一的差别在于它允許鍵值重複,是以它的插入操作采用的是底層機制 RB-tree 的 insert_equal() 而非 insert_unique。

unordered_set

unordered_set是基于哈希表,是以要了解unordered_set,就必須了解哈希表的機制。哈希表是根據關鍵碼值而進行直接通路的資料結構,通過相應的哈希函數(也稱散列函數)處理關鍵字得到相應的關鍵碼值,關鍵碼值對應着一個特定位置,用該位置來存取相應的資訊,這樣就能以較快的速度擷取關鍵字的資訊。

template < class Key,  
    class Hash = hash<Key>,  
    class Pred = equal_to<Key>,  
    class Alloc = allocator<Key>  
> class unordered_set;      

4 算法

簡單查找算法,要求輸入疊代器(input iterator)

find(beg, end, val); // 傳回一個疊代器,指向輸入序列中第一個等于 val 的元素,未找到傳回 end

find_if(beg, end, unaryPred); // 傳回一個疊代器,指向第一個滿足 unaryPred 的元素,未找到傳回 end

find_if_not(beg, end, unaryPred); // 傳回一個疊代器,指向第一個令 unaryPred 為 false 的元素,未找到傳回 end

count(beg, end, val); // 傳回一個計數器,指出 val 出現了多少次

count_if(beg, end, unaryPred); // 統計有多少個元素滿足 unaryPred

all_of(beg, end, unaryPred); // 傳回一個 bool 值,判斷是否所有元素都滿足 unaryPred

any_of(beg, end, unaryPred); // 傳回一個 bool 值,判斷是否任意(存在)一個元素滿足 unaryPred

none_of(beg, end, unaryPred); // 傳回一個 bool 值,判斷是否所有元素都不滿足 unaryPred

查找重複值的算法,傳入向前疊代器(forward iterator)

adjacent_find(beg, end); // 傳回指向第一對相鄰重複元素的疊代器,無相鄰元素則傳回 end

adjacent_find(beg, end, binaryPred); // 傳回指向第一對相鄰重複元素的疊代器,無相鄰元素則傳回 end

search_n(beg, end, count, val); // 傳回一個疊代器,從此位置開始有 count 個相等元素,不存在則傳回 end

search_n(beg, end, count, val, binaryPred); // 傳回一個疊代器,從此位置開始有 count 個相等元素,不存在則傳回 end

查找子序列算法,除 find_first_of(前兩個輸入疊代器,後兩個前向疊代器) 外,都要求兩個前向疊代器

search(beg1, end1, beg2, end2); // 傳回第二個輸入範圍(子序列)在爹一個輸入範圍中第一次出現的位置,未找到則傳回 end1

search(beg1, end1, beg2, end2, binaryPred); // 傳回第二個輸入範圍(子序列)在爹一個輸入範圍中第一次出現的位置,未找到則傳回 end1

find_first_of(beg1, end1, beg2, end2); // 傳回一個疊代器,指向第二個輸入範圍中任意元素在第一個範圍中首次出現的位置,未找到則傳回end1

find_first_of(beg1, end1, beg2, end2, binaryPred); // 傳回一個疊代器,指向第二個輸入範圍中任意元素在第一個範圍中首次出現的位置,未找到則傳回end1

find_end(beg1, end1, beg2, end2); // 類似 search,但傳回的最後一次出現的位置。如果第二個輸入範圍為空,或者在第一個輸入範圍為空,或者在第一個輸入範圍中未找到它,則傳回 end1

find_end(beg1, end1, beg2, end2, binaryPred); // 類似 search,但傳回的最後一次出現的位置。如果第二個輸入範圍為空,或者在第一個輸入範圍為空,或者在第一個輸入範圍中未找到它,則傳回 end1

其他隻讀算法,傳入輸入疊代器

for_each(beg, end, unaryOp); // 對輸入序列中的每個元素應用可調用對象 unaryOp,unaryOp 的傳回值被忽略

mismatch(beg1, end1, beg2); // 比較兩個序列中的元素。傳回一個疊代器的 pair,表示兩個序列中第一個不比對的元素

mismatch(beg1, end1, beg2, binaryPred); // 比較兩個序列中的元素。傳回一個疊代器的 pair,表示兩個序列中第一個不比對的元素

equal(beg1, end1, beg2); // 比較每個元素,确定兩個序列是否相等。

equal(beg1, end1, beg2, binaryPred); // 比較每個元素,确定兩個序列是否相等。

二分搜尋算法,傳入前向疊代器或随機通路疊代器(random-access iterator),要求序列中的元素已經是有序的

lower_bound(beg, end, val); // 傳回一個非遞減序列 [beg, end) 中的第一個大于等于值 val 的位置的疊代器,不存在則傳回 end

lower_bound(beg, end, val, comp); // 傳回一個非遞減序列 [beg, end) 中的第一個大于等于值 val 的位置的疊代器,不存在則傳回 end

upper_bound(beg, end, val); // 傳回一個非遞減序列 [beg, end) 中第一個大于 val 的位置的疊代器,不存在則傳回 end

upper_bound(beg, end, val, comp); // 傳回一個非遞減序列 [beg, end) 中第一個大于 val 的位置的疊代器,不存在則傳回 end

equal_range(beg, end, val); // 傳回一個 pair,其 first 成員是 lower_bound 傳回的疊代器,其 second 成員是 upper_bound 傳回的疊代器

binary_search(beg, end, val); // 傳回一個 bool 值,指出序列中是否包含等于 val 的元素。對于兩個值 x 和 y,當 x 不小于 y 且 y 也不小于 x 時,認為它們相等。

隻寫不讀算法,要求輸出疊代器(output iterator)

fill(beg, end, val); // 将 val 賦予每個元素,傳回 void

fill_n(beg, cnt, val); // 将 val 賦予 cnt 個元素,傳回指向寫入到輸出序列最有一個元素之後位置的疊代器

genetate(beg, end, Gen); // 每次調用 Gen() 生成不同的值賦予每個序列,傳回 void

genetate_n(beg, cnt, Gen); // 每次調用 Gen() 生成不同的值賦予 cnt 個序列,傳回指向寫入到輸出序列最有一個元素之後位置的疊代器

7.使用輸入疊代器的寫算法,讀取一個輸入序列,将值寫入到一個輸出序列(dest)中

copy(beg, end, dest); // 從輸入範圍将元素拷貝所有元素到 dest 指定定的目的序列

copy_if(beg, end, dest, unaryPred); // 從輸入範圍将元素拷貝滿足 unaryPred 的元素到 dest 指定定的目的序列

copy_n(beg, n, dest); // 從輸入範圍将元素拷貝前 n 個元素到 dest 指定定的目的序列

move(beg, end, dest); // 對輸入序列中的每個元素調用 std::move,将其移動到疊代器 dest 開始始的序列中

transform(beg, end, dest, unaryOp); // 調用給定操作(一進制操作),并将結果寫到dest中

transform(beg, end, beg2, dest, binaryOp); // 調用給定操作(二進制操作),并将結果寫到dest中

replace_copy(beg, end, dest, old_val, new_val); // 将每個元素拷貝到 dest,将等于 old_val 的的元素替換為 new_val

replace_copy_if(beg, end, dest, unaryPred, new_val); // 将每個元素拷貝到 dest,将滿足 unaryPred 的的元素替換為 new_val

merge(beg1, end1, beg2, end2, dest); // 兩個輸入序列必須都是有序的,用小于号運算符将合并後的序列寫入到 dest 中

merge(beg1, end1, beg2, end2, dest, comp); // 兩個輸入序列必須都是有序的,使用給定的比較操作(comp)将合并後的序列寫入到 dest 中

8.劃分算法,要求雙向選代器(bidirectional iterator)

is_partitioned(beg, end, unaryPred); // 如果所有滿足謂詞 unaryPred 的元素都在不滿足 unarypred 的元素之前,則傳回 true。若序列為空,也傳回 true

partition_copy(beg, end, dest1, dest2, unaryPred); // 将滿足 unaryPred 的元素拷貝到到 dest1,并将不滿足 unaryPred 的元素拷貝到到 dest2。傳回一個疊代器 pair,其 first 成員表示拷貝到 dest1 的的元素的末尾,second 表示拷貝到 dest2 的元素的末尾。

partitioned_point(beg, end, unaryPred); // 輸入序列必須是已經用 unaryPred 劃分過的。傳回滿足  unaryPred 的範圍的尾後疊代器。如果傳回的疊代器不是 end,則它指向的元素及其後的元素必須都不滿足 unaryPred

stable_partition(beg, end, unaryPred); // 使用 unaryPred 劃分輸入序列。滿足 unaryPred 的元素放置在序列開始,不滿足的元素放在序列尾部。傳回一個疊代器,指向最後一個滿足 unaryPred 的元素之後的位置如果所有元素都不滿足 unaryPred,則傳回 beg

partition(beg, end, unaryPred); // 使用 unaryPred 劃分輸入序列。滿足 unaryPred 的元素放置在序列開始,不滿足的元素放在序列尾部。傳回一個疊代器,指向最後一個滿足 unaryPred 的元素之後的位置如果所有元素都不滿足 unaryPred,則傳回 beg

排序算法,要求随機通路疊代器(random-access iterator)

sort(beg, end); // 排序整個範圍
stable_sort(beg, end); // 排序整個範圍(穩定排序)
sort(beg, end, comp); // 排序整個範圍
stable_sort(beg, end, comp); // 排序整個範圍(穩定排序)
is_sorted(beg, end); // 傳回一個 bool 值,指出整個輸入序列是否有序
is_sorted(beg, end, comp); // 傳回一個 bool 值,指出整個輸入序列是否有序
is_sorted_until(beg, end); // 在輸入序列中査找最長初始有序子序列,并傳回子序列的尾後疊代器
is_sorted_until(beg, end, comp); // 在輸入序列中査找最長初始有序子序列,并傳回子序列的尾後疊代器
partial_sort(beg, mid, end); // 排序 mid-beg 個元素。即,如果 mid-beg 等于 42,則此函數将值最小的 42 個元素有序放在序列前 42 個位置
partial_sort(beg, mid, end, comp); // 排序 mid-beg 個元素。即,如果 mid-beg 等于 42,則此函數将值最小的 42 個元素有序放在序列前 42 個位置
partial_sort_copy(beg, end, destBeg, destEnd); // 排序輸入範圍中的元素,并将足夠多的已排序元素放到 destBeg 和 destEnd 所訓示的序列中
partial_sort_copy(beg, end, destBeg, destEnd, comp); // 排序輸入範圍中的元素,并将足夠多的已排序元素放到 destBeg 和 destEnd 所訓示的序列中
nth_element(beg, nth, end); // nth 是一個疊代器,指向輸入序列中第 n 大的元素。nth 之前的元素都小于等于它,而之後的元素都大于等于它
nth_element(beg, nth, end, comp); // nth 是一個疊代器,指向輸入序列中第 n 大的元素。nth 之前的元素都小于等于它,而之後的元素都大于等于它
使用前向疊代器的重排算法。普通版本在輸入序列自身内部重拍元素,_copy 版本完成重拍後寫入到指定目的序列中,而不改變輸入序列
remove(beg, end, val); // 通過用保留的元素覆寫要删除的元素實作删除 ==val 的元素,傳回一個指向最後一個删除元素的尾後位置的疊代器
remove_if(beg, end, unaryPred); // 通過用保留的元素覆寫要删除的元素實作删除滿足 unaryPred 的元素,傳回一個指向最後一個删除元素的尾後位置的疊代器
remove_copy(beg, end, dest, val); // 通過用保留的元素覆寫要删除的元素實作删除 ==val 的元素,傳回一個指向最後一個删除元素的尾後位置的疊代器
remove_copy_if(beg, end, dest, unaryPred); // 通過用保留的元素覆寫要删除的元素實作删除滿足 unaryPred 的元素,傳回一個指向最後一個删除元素的尾後位置的疊代器
unique(beg, end); // 通過對覆寫相鄰的重複元素(用 == 确定是否相同)實作重排序列。傳回一個疊代器,指向不重複元素的尾後位置
unique (beg, end, binaryPred); // 通過對覆寫相鄰的重複元素(用 binaryPred 确定是否相同)實作重排序列。傳回一個疊代器,指向不重複元素的尾後位置
unique_copy(beg, end, dest); // 通過對覆寫相鄰的重複元素(用 == 确定是否相同)實作重排序列。傳回一個疊代器,指向不重複元素的尾後位置
unique_copy_if(beg, end, dest, binaryPred); // 通過對覆寫相鄰的重複元素(用 binaryPred 确定是否相同)實作重排序列。傳回一個疊代器,指向不重複元素的尾後位置
rotate(beg, mid, end); // 圍繞 mid 指向的元素進行元素轉動。元素 mid 成為為首元素,随後是 mid+1 到到 end 之前的元素,再接着是 beg 到 mid 之前的元素。傳回一個疊代器,指向原來在 beg 位置的元素
rotate_copy(beg, mid, end, dest); // 圍繞 mid 指向的元素進行元素轉動。元素 mid 成為為首元素,随後是 mid+1 到到 end 之前的元素,再接着是 beg 到 mid 之前的元素。傳回一個疊代器,指向原來在 beg 位置的元素
使用雙向疊代器的重排算法
reverse(beg, end); // 翻轉序列中的元素,傳回 void
reverse_copy(beg, end, dest);; // 翻轉序列中的元素,傳回一個疊代器,指向拷貝到目的序列的元素的尾後位置
使用随機通路疊代器的重排算法
random_shuffle(beg, end); // 混洗輸入序列中的元素,傳回 void
random_shuffle(beg, end, rand); // 混洗輸入序列中的元素,rand 接受一個正整數的随機對象,傳回 void
shuffle(beg, end, Uniform_rand); // 混洗輸入序列中的元素,Uniform_rand 必須滿足均勻分布随機數生成器的要求,傳回 void      

最小值和最大值

min(val1, va12); // 傳回 val1 和 val2 中的最小值,兩個實參的類型必須完全一緻。參數和傳回類型都是 const的引引用,意味着對象不會被拷貝。下略
min(val1, val2, comp);
min(init_list);
min(init_list, comp);
max(val1, val2);
max(val1, val2, comp);
max(init_list);
max(init_list, comp);
minmax(val1, val2); // 傳回一個 pair,其 first 成員為提供的值中的較小者,second 成員為較大者。下略
minmax(vall, val2, comp);
minmax(init_list);
minmax(init_list, comp);
min_element(beg, end); // 傳回指向輸入序列中最小元素的疊代器
min_element(beg, end, comp); // 傳回指向輸入序列中最小元素的疊代器
max_element(beg, end); // 傳回指向輸入序列中最大元素的疊代器
max_element(beg, end, comp); // 傳回指向輸入序列中最大元素的疊代器
minmax_element(beg, end); // 傳回一個 pair,其中 first 成員為最小元素,second 成員為最大元素
minmax_element(beg, end, comp); // 傳回一個 pair,其中 first 成員為最小元素,second 成員為最大元素
字典序比較,根據第一對不相等的元素的相對大小來傳回結果。如果第一個序列在字典序中小于第二個序列,則傳回 true。否則,傳回 fa1se。如果個序列比另一個短,且所有元素都與較長序列的對應元素相等,則較短序列在字典序中更小。如果序列長度相等,且對應元素都相等,則在字典序中任何一個都不大于另外一個。
lexicographical_compare(beg1, end1, beg2, end2);
lexicographical_compare(beg1, end1, beg2, end2, comp);      

5 如何選擇合适的容器

需要根據容器的特點和使用場景而定,可能滿足需求的不止一種容器。

按是否有序關聯性分為:

序列式容器:array、vector、deque、list 和 forward_list;

關聯式容器:map、multimap、set 和 multiset;

無序關聯式容器:unordered_map、unordered_multimap、unordered_set 和 unordered_multiset;

容器擴充卡:stack、queue 和 priority_queue。 根據容器底層采用是否是連續的存儲空間分為:

采用連續的存儲空間:array、vector、deque;

采用分散的存儲空間:list、forward_list 以及所有的關聯式容器和哈希容器。

注意:deque 容器歸為使用連續存儲空間的這一類,是存在争議的。因為 deque 容器底層采用一段一段的連續空間存儲元素,但是各段存儲空間之間并不一定是緊挨着的。

全網最全面的STL總結與常見面試題,值得收藏(三)

選擇容器流程圖(來源于網絡)

選擇容器的幾點建議:

如果隻是存儲确定或不确定的對象,而不去删除它們,可以選用vector。就是因為vector是數組的替代品,是連續記憶體的,不适合頻繁的删除。

如果在容器的指定位置插入新元素,則隻能選擇序列式容器,不選擇關聯式容器和哈希容器。

如果頻繁的插入和删除,可以選用list(連結清單),記憶體不是連續的,可以友善的插入和删除,但是不支援索引通路。

資料量很大,不在乎他們的排序,要求效率,對容器中各元素的存儲位置沒有要求,可以考慮使用哈希容器,反之就要避免使用哈希容器。

如果是随機通路疊代器,選擇 array、vector、deque。

如果是雙向疊代器,考慮 list 序列式容器以及所有的關聯式容器。

如果必須是前向疊代器,考慮 forward_list序列式容器以及所有的哈希容器。

如果插入或删除操作時,容器中的其它元素不移動?選擇不是array、vector、deque的其它容器。

6 面試中常出現的STL問題

vector的底層原理

vector底層是一個動态數組,包含三個疊代器,start和finish之間是已經被使用的空間範圍,end_of_storage是整塊連續空間包括備用空間的尾部。

當空間不夠裝下資料(vec.push_back(val))時,會自動申請另一片更大的空間(1.5倍或者2倍),然後把原來的資料拷貝到新的記憶體空間,接着釋放原來的那片空間【vector記憶體增長機制】。

當釋放或者删除(vec.clear())裡面的資料時,其存儲空間不釋放,僅僅是清空了裡面的資料。

是以,對vector的任何操作一旦引起了空間的重新配置,指向原vector的所有疊代器會都失效了

全網最全面的STL總結與常見面試題,值得收藏(三)

vector中的reserve和resize的差別

reserve是直接擴充到已經确定的大小,可以減少多次開辟、釋放空間的問題(優化push_back),就可以提高效率,其次還可以減少多次要拷貝資料的問題。reserve隻是保證vector中的空間大小(capacity)最少達到參數所指定的大小n。reserve()隻有一個參數。

resize()可以改變有效空間的大小,也有改變預設值的功能。capacity的大小也會随着改變。resize()可以有多個參數。

vector中的size和capacity的差別

size表示目前vector中有多少個元素(finish - start);

capacity函數則表示它已經配置設定的記憶體中可以容納多少元素(end_of_storage - start);

vector中erase方法與algorithn中的remove方法差別

vector中erase方法真正删除了元素,疊代器不能通路了

remove隻是簡單地将元素移到了容器的最後面,疊代器還是可以通路到。因為algorithm通過疊代器進行操作,不知道容器的内部結構,是以無法進行真正的删除。

vector疊代器失效的情況

當插入一個元素到vector中,由于引起了記憶體重新配置設定,是以指向原記憶體的疊代器全部失效。

當删除容器中一個元素後,該疊代器所指向的元素已經被删除,那麼也造成疊代器失效。erase方法會傳回下一個有效的疊代器,是以當我們要删除某個元素時,需要it=vec.erase(it);。

正确釋放vector的記憶體(clear(), swap(), shrink_to_fit())

vec.clear():清空内容,但是不釋放記憶體。

vector().swap(vec):清空内容,且釋放記憶體,想得到一個全新的vector。

vec.shrink_to_fit():請求容器降低其capacity和size比對。

vec.clear();vec.shrink_to_fit();:清空内容,且釋放記憶體。

list的底層原理

全網最全面的STL總結與常見面試題,值得收藏(三)

ist的底層是一個雙向連結清單,使用連結清單存儲資料,并不會将它們存儲到一整塊連續的記憶體空間中。恰恰相反,各元素占用的存儲空間(又稱為節點)是獨立的、分散的,它們之間的線性關系通過指針來維持,每次插入或删除一個元素,就配置或釋放一個元素空間。

list不支援随機存取,如果需要大量的插入和删除,而不關心随即存取

什麼情況下用vector,什麼情況下用list,什麼情況下用deque

vector可以随機存儲元素(即可以通過公式直接計算出元素位址,而不需要挨個查找),但在非尾部插入删除資料時,效率很低,适合對象簡單,對象數量變化不大,随機通路頻繁。除非必要,我們盡可能選擇使用vector而非deque,因為deque的疊代器比vector疊代器複雜很多。

list不支援随機存儲,适用于對象大,對象數量變化頻繁,插入和删除頻繁,比如寫多讀少的場景。

需要從首尾兩端進行插入或删除操作的時候需要選擇deque。

priority_queue的底層原理

priority_queue:優先隊列,其底層是用堆來實作的。在優先隊列中,隊首元素一定是目前隊列中優先級最高的那一個。

map 、set、multiset、multimap的底層原理

map 、set、multiset、multimap的底層實作都是紅黑樹,epoll模型的底層資料結構也是紅黑樹,linux系統中CFS程序排程算法,也用到紅黑樹。

全網最全面的STL總結與常見面試題,值得收藏(三)

紅黑樹的特性:

每個結點或是紅色或是黑色;

根結點是黑色;

每個葉結點是黑的;

如果一個結點是紅的,則它的兩個兒子均是黑色;

每個結點到其子孫結點的所有路徑上包含相同數目的黑色結點。

為何map和set的插入删除效率比其他序列容器高

因為不需要記憶體拷貝和記憶體移動

為何map和set每次Insert之後,以前儲存的iterator不會失效?

因為插入操作隻是結點指針換來換去,結點記憶體沒有改變。而iterator就像指向結點的指針,記憶體沒變,指向記憶體的指針也不會變。

當資料元素增多時(從10000到20000),map的set的查找速度會怎樣變化?

RB-TREE用二分查找法,時間複雜度為logn,是以從10000增到20000時,查找次數從log10000=14次到log20000=15次,多了1次而已。

map 、set、multiset、multimap的特點

set和multiset會根據特定的排序準則自動将元素排序,set中元素不允許重複,multiset可以重複。

map和multimap将key和value組成的pair作為元素,根據key的排序準則自動将元素排序(因為紅黑樹也是二叉搜尋樹,是以map預設是按key排序的),map中元素的key不允許重複,multimap可以重複。

map和set的增删改查速度為都是logn,是比較高效的。

為何map和set的插入删除效率比其他序列容器高,而且每次insert之後,以前儲存的iterator不會失效?

存儲的是結點,不需要記憶體拷貝和記憶體移動。

插入操作隻是結點指針換來換去,結點記憶體沒有改變。而iterator就像指向結點的指針,記憶體沒變,指向記憶體的指針也不會變。

為何map和set不能像vector一樣有個reserve函數來預配置設定資料?

在map和set内部存儲的已經不是元素本身了,而是包含元素的結點。也就是說map内部使用的Alloc并不是map聲明的時候從參數中傳入的Alloc。

set的底層實作實作為什麼不用哈希表而使用紅黑樹?

set中元素是經過排序的,紅黑樹也是有序的,哈希是無序的

如果隻是單純的查找元素的話,那麼肯定要選哈希表了,因為哈希表在的最好查找時間複雜度為O(1),并且如果用到set中那麼查找時間複雜度的一直是O(1),因為set中是不允許有元素重複的。而紅黑樹的查找時間複雜度為O(lgn)

hash_map與map的差別?什麼時候用hash_map,什麼時候用map? 構造函數:hash_map需要hash function和等于函數,而map需要比較函數(大于或小于)。

存儲結構:hash_map以hashtable為底層,而map以RB-TREE為底層。

總的說來,hash_map查找速度比map快,而且查找速度基本和資料量大小無關,屬于常數級别。而map的查找速度是logn級别。但不一定常數就比log小,而且hash_map還有hash function耗時。

如果考慮效率,特别當元素達到一定數量級時,用hash_map。

考慮記憶體,或者元素數量較少時,用map。

疊代器失效的問題

插入操作:

對于vector和string,如果容器記憶體被重新配置設定,iterators,pointers,references失效;如果沒有重新配置設定,那麼插入點之前的iterator有效,插入點之後的iterator失效;

對于deque,如果插入點位于除front和back的其它位置,iterators,pointers,references失效;當我們插入元素到front和back時,deque的疊代器失效,但reference和pointers有效;

對于list和forward_list,所有的iterator,pointer和refercnce有效。

删除操作:

對于vector和string,删除點之前的iterators,pointers,references有效;off-the-end疊代器總是失效的;

對于deque,如果删除點位于除front和back的其它位置,iterators,pointers,references失效;當我們插入元素到front和back時,off-the-end失效,其他的iterators,pointers,references有效;

對于關聯容器map來說,如果某一個元素已經被删除,那麼其對應的疊代器就失效了,不應該再被使用,否則會導緻程式無定義的行為。

線程不安全的情況

在對同一個容器進行多線程的讀寫、寫操作時;

在每次調用容器的成員函數期間都要鎖定該容器;

在每個容器傳回的疊代器(例如通過調用begin或end)的生存期之内都要鎖定該容器;

在每個在容器上調用的算法執行期間鎖定該容器。

參考資料

http://www.cplusplus.com/reference/stl/ https://blog.csdn.net/qq_23350817/article/details/87930715 https://blog.csdn.net/bizhu12/article/details/6769976 http://c.biancheng.net/view/7385.html https://blog.csdn.net/daaikuaichuan/article/details/80717222