c++ std中set與unordered_set區別和map與unordered_map區別類似,其底層的數據結構說明如下:
1、set基於紅黑樹實現,紅黑樹具有自動排序的功能,因此map內部所有的數據,在任何時候,都是有序的。
2、unordered_set基於哈希表,數據插入和查找的時間復雜度很低,幾乎是常數時間,而代價是消耗比較多的內存,無自動排序功能。底層實現上,使用一個下標范圍比較大的數組來存儲元素,形成很多的桶,利用hash函數對key進行映射到不同區域進行保存。
更詳細的區別,如下圖:
set與unordered相比:
1、set比unordered_set使用更少的內存來存儲相同數量的元素。
2、對於少量的元素,在set中查找可能比在unordered_set中查找更快。
3、盡管許多操作在unordered_set的平均情況下更快,但通常需要保證set在最壞情況下有更好的復雜度(例如insert)。
4、如果您想按順序訪問元素,那么set對元素進行排序的功能是很有用的。
5、您可以用<、<=、>和>=從字典順序上比較不同的set集。unordered_set集則不支持這些操作。
一般來說,在如下情況,適合使用set:
1、我們需要有序的數據(不同元素)。
2、我們必須打印/訪問數據(按排序順序)。
3、我們需要知道元素的前任/繼承者。
一般來說,在如下情況,適合使用unordered_set:
1、我們需要保留一組元素,不需要排序。
2、我們需要單元素訪問,即不需要遍歷。
3、僅僅只是插入、刪除、查找的話。
示例:
set:
Input : 1, 8, 2, 5, 3, 9 Output : 1, 2, 3, 5, 8, 9
unordered_set:
Input : 1, 8, 2, 5, 3, 9 Output : 9 3 1 8 2 5 (順序依賴於 hash function)
下面再給出一個以vector<int>為key的示例,對比下set與unordered_set:
1 set<vector<int>> s; 2 s.insert({1, 2}); 3 s.insert({1, 3}); 4 s.insert({1, 2}); 5 6 for(const auto& vec:s) 7 cout<<vec<<endl; 8 // 1 2 9 // 1 3
因為vector重載了operator<,因此可以作為set的key。
但是如果直接使用unordered_set<vector<int>> s;則報錯,因為vector沒有hash函數,需要自己定義一個,可以定義一個類似下面這樣的hash函數:
1 struct VectorHash { 2 size_t operator()(const std::vector<int>& v) const { 3 std::hash<int> hasher; 4 size_t seed = 0; 5 for (int i : v) { 6 seed ^= hasher(i) + 0x9e3779b9 + (seed<<6) + (seed>>2); 7 } 8 return seed; 9 } 10 };
接下來這樣使用:
1 unordered_set<vector<int>, VectorHash> s; 2 s.insert({1, 2}); 3 s.insert({1, 3}); 4 s.insert({1, 2}); 5 6 for(const auto& vec:s) 7 cout<<vec<<endl; 8 // 1 2 9 // 1 3
或者模板特化struct hash<std::vector<int>>
1 namespace std { 2 template<> 3 struct hash<std::vector<int>> { 4 size_t operator()(const vector<int> &v) const { 5 std::hash<int> hasher; 6 size_t seed = 0; 7 for (int i : v) { 8 seed ^= hasher(i) + 0x9e3779b9 + (seed << 6) + (seed >> 2); 9 } 10 return seed; 11 } 12 }; 13 } 14 15 // usage example 16 void test_unordered_set(){ 17 unordered_set<std::vector<int>> s; 18 s.insert({1, 2}); 19 s.insert({1, 3}); 20 s.insert({1, 2}); 21 for(const auto& vec:s) 22 cout<<vec<<endl; 23 // 1 3 24 // 1 2 25 26 std::hash<int> hasher; 27 cout<<"hasher(99): "<<hasher(99)<<" ,hasher(77): "<<hasher(77)<<endl; 28 // hasher(99): 99 ,hasher(77): 77 29 }
可以看到,在某些情況下,unordered_set的使用門檻還是挺高的。
Input : 1, 8, 2, 5, 3, 9
Output : 1, 2, 3, 5, 8, 9