某日二師兄參加XXX科技公司的C++工程師開發崗位第27面:
面試官:用過
std::set/std::map
嗎?二師兄:用過。
面試官:能介紹一下二者嗎?
二師兄:
std::set
是一個有序的集合,其中的元素是唯一的,即每個元素只能出現一次。一般用於去重和自動排序。二師兄:
std::map
同樣是有序組合,只不過它不止有key
,每個key
還對用一個value
。其中key
是唯一,不可重複,但是value
卻沒有限制。key/value
也被稱為鍵值對。面試官:知道他們底層使用什麼資料結構儲存資料的嗎?
二師兄:兩者都是使用紅黑樹作為底層的資料結構。紅黑樹是一種自動平衡的二元樹,它確保插入、刪除和查詢操作的時間複雜度都是
O(log n)
。面試官:
set/map
對於key
的型別有什麼要求嗎?二師兄:因為
set/map被
稱為有序容器,所以對插入進去的key
有排序的要求。一般需要為型別實現<
比較方法,以下程式碼無法通過編譯:
#include <iostream>
#include <set>
struct Foo
{
Foo(int v):val(v){}
int val;
};
int main(int argc, char const *argv[])
{
std::set<Foo> iset;
iset.insert(Foo(1024));
iset.insert(Foo(42));
for(auto it = iset.begin(); it != iset.end(); ++it)
{
std::cout << it->val << std::endl;
}
return 0;
}
二師兄:此時需要為
Foo
型別實現bool operator<(const T&, const T&)
函數,才能通過編譯:
bool operator<(const Foo& lhs,const Foo& rhs) {return lhs.val < rhs.val;}
面試官:按照你的方法,可以實現從小到大的排序。如何實現從大到小的排序?
二師兄:
set/map
類別範本的第二個模板引數可以傳入比較型別,預設比較型別是std::less<_Key>
,我們可以傳入std::greater<T>
,此時需要實現bool operator>(const T&, const T&)
函數。二師兄:還有一種方法是手寫一個仿函數,過載
bool operator()(const T, const T) const
函數用於比較兩者的大小:
struct Comp
{
bool operator()(const Foo& lhs, const Foo& rhs) const
{
return lhs.val > rhs.val;
}
};
std::set<Foo,Comp> iset;
面試官:可以修改
map
中的key
嗎?二師兄:不可以。因為
map
中的key
是const
的。強制修改(取地址,const_cast
轉非const
指標,解除參照賦值)會造未知的錯誤。面試官:當
map
中不存在某個key
時,對map
使用map[key]
操作會有什麼後果?二師兄:會在
map
中增加一個鍵值對,鍵名為key
,值是傳入的value
型別的預設值。面試官:如果不希望刪除重複的
key
,有什麼辦法?二師兄:STL中提供了
std::multiset
和std::multimap
兩個容器,可以存入key
相同的多個元素。面試官:在
std::multimap
中如何通過key
查詢value
?二師兄:
multimap
提供了equal_range
方法,此方法返回一個pair
,分別對應2
個迭代器。通過迴圈迭代器來獲取key
對應的所有value
。
#include <iostream>
#include <map>
int main() {
std::multimap<int, std::string> mmap;
mmap.insert(std::make_pair(1, "1"));
mmap.insert(std::make_pair(2, "2"));
mmap.insert(std::make_pair(3, "3"));
mmap.insert(std::make_pair(1, "1"));
auto range = mmap.equal_range(1);
for (auto it = range.first; it != range.second; ++it) {
std::cout << it->second << std::endl;
}
return 0;
}
面試官:最後一個問題,你覺得單純的查詢而言,是
vector
快還是map
快?二師兄:當然是
map
快,因為vector
的查詢的時間複雜度是O(n)
,而map是O(logn)
。面試官:好的,今天面試結束了,回去等通知吧。
讓我們看看最後一個問題:
單純的查詢而言,是
vector
快還是map
快?
這裡二師兄的是標準答案,實際上當資料量特別大的時候,的確map
是更好的選擇。
但當資料量沒那麼大的時候(少於1000
條記錄),vector
要比map
查詢速度快。原因我們在之前的面試文章中講過,vector
記憶體連續,快取更友好。map
底層是紅黑樹,記憶體並不連續。
當資料量小的時候,演演算法的優勢沒有抵消快取的劣勢,所以vector
在資料量小的時候更勝一籌。
「紙上得來終覺淺,絕知此事要躬行」。小夥伴們,一起努力吧!
關注我,帶你21天「精通」C++!(狗頭)