STL的容器map為我們處理有序key-value形式數據提供了非常大的便利,由於內部紅黑樹結構的存儲,查找的時間復雜度為O(log2N)。
一般而言,使用map的時候直接采取map<typename A, typename B>的形式即可,map的內部實現默認使用A類型變量的升序來排序map的值。
但是有時我們需要對map的值做特殊的排序(不經其他容器的輔助),這就需要在定義map變量時做些特殊的處理。
STL中map的定義是:
1 template<class _Kty, 2 class _Ty, 3 class _Pr = less<_Kty>, 4 class _Alloc = allocator<pair<const _Kty, _Ty>>> 5 class map 6 : public _Tree<_Tmap_traits<_Kty, _Ty, _Pr, _Alloc, false>> 7 {
這是一個模板類,我們的特殊處理主要改造的就是class _Pr = less<_Kty>,並且從這里我們也能看到,無論做哪種修改,排序都是針對key而言的,要實現value的自定義排序,
不是修改_Pr類型能完成的。
替換_Pr的也必須是一個類型,即至少我們要自己創建一個類型,用來做key的比較。自然,我們需要做的是重載函數調用操作符"()",一般的形式為
1 class T{ 2 public: 3 bool operator()(const T& lhs, const T& rhs)const 4 { 5 ... 6 } 7 };
代碼需包含頭文件<algorithm>、<functional>。
下面是常見的一些自定義排序:
a.對基本類型的key以降序排列
map默認提供的是less<_Kty>類型的排序方式,閱讀STL源碼
1 template<class _Ty = void> 2 struct less 3 { // functor for operator< 4 _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty first_argument_type; 5 _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty second_argument_type; 6 _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef bool result_type; 7 8 constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const 9 { // apply operator< to operands 10 return (_Left < _Right); 11 } 12 };
修改上述代碼的第10行,為修改后的類型起一個自定義名字很簡單,不過STL已經為我們提供了整個類型定義:
1 template<class _Ty = void> 2 struct greater 3 { // functor for operator> 4 _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty first_argument_type; 5 _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty second_argument_type; 6 _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef bool result_type; 7 8 constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const 9 { // apply operator> to operands 10 return (_Left > _Right); 11 } 12 };
我們直接使用就行:
1 std::map<int, int, std::greater<int>> mi; 2 for (int i = 0; i < 5; i++) 3 { 4 mi[i] = i * 2; 5 } 6 7 std::for_each(mi.begin(), mi.end(), 8 [](const std::map<int, int, std::greater<int>>::value_type& vl) { 9 cout << "key:" << vl.first << " value:" << vl.second << '\n'; 10 });
對應的輸出為:

這里,我們實現了按key降序排列的目的。
b.為自定義類型的key做排序:
自定義類型的key定義map時(使用map默認排序),我們一般都要做一件事:為自定義類型重載“<”操作符,顯然,這是為了map創建對象時可以使用less。
因此,我們替換less<_Kty>也同樣是要做這樣的事:自定義排序規則, 比如:
1 class MyKey { 2 public: 3 MyKey(int fidx = 0, int sidx = 0) 4 :m_firstIdx(fidx), m_secondIdx(sidx) {} 5 6 int m_firstIdx; 7 int m_secondIdx; 8 }; 9 10 class MyCompare{ 11 public: 12 bool operator()(const MyKey& lhs, const MyKey& rhs)const 13 { 14 if (lhs.m_firstIdx > rhs.m_firstIdx) 15 { 16 return true; 17 } 18 else if (lhs.m_firstIdx == rhs.m_firstIdx) 19 { 20 return lhs.m_secondIdx > rhs.m_secondIdx; 21 } 22 return false; 23 } 24 }; 25 26 class MyCompare2 { 27 public: 28 bool operator()(const MyKey& lhs, const MyKey& rhs)const 29 { 30 return lhs.m_firstIdx > rhs.m_firstIdx; 31 } 32 };
使用MyCompare:
1 std::map<MyKey, int, MyCompare> mi; 2 for (int i = 0; i < 5; i++) 3 { 4 mi[MyKey(i * 2, i)] = i * 2; 5 } 6 7 std::for_each(mi.begin(), mi.end(), 8 [](const std::map<MyKey, int, MyCompare>::value_type& vl) { 9 cout << "key:" << vl.first.m_firstIdx << "-" << vl.first.m_secondIdx << " value:" << vl.second << '\n'; 10 });
使用MyCompare2:
1 std::map<MyKey, int, MyCompare2> mi; 2 for (int i = 0; i < 5; i++) 3 { 4 mi[MyKey(i * 2, i)] = i * 2; 5 } 6 7 std::for_each(mi.begin(), mi.end(), 8 [](const std::map<MyKey, int, MyCompare2>::value_type& vl) { 9 cout << "key:" << vl.first.m_firstIdx << "-" << vl.first.m_secondIdx << " value:" << vl.second << '\n'; 10 });
以上兩種有相同的輸出:

我們實現了自定義類型自定義排序的目的。
可以看到,使用map其實有很大的自由度,我們完全可以定制自己的map,為我們解決問題、精簡代碼帶來很大的便利。
