眾所周知,balabalabalabala············。
所以掌握sort函數(庫文件:<algorithm>)的用法還是很有必要的。
一般選手只會簡單地用用sort排一排數組之類,但是一旦掌握了sort的精髓cmp函數(也有叫comp,名字不重要)的重構,sort函數也可以玩得出神入化。
這里只是不全面地記錄下了在切題的過程中遇到的重構cmp的應用,僅供參考:
一、cmp函數的原理探究
研究sort的底層代碼就會知道,sort函數非常強大,內部結合了多種排序算法以達到相對穩定的高效。但是不管排序的策略如何,其中都用一個“比較”的步驟。而cmp函數(全稱大概代表了單詞compare,表示“比較”的意思)便定義了這個比較的標准。
舉個栗子:當比較一個數組中a、b(a<b)兩個元素的時候,通過cmp函數,我們可以決定a與b比較的結果。比如是小的放在前面(那么比較后a與b的相對位置就是···a···b···(一般sort默認就是非降序)),或者大的放在前面(那么比較后a與b的相對位置就是···b···a···)
怎么把我們對cmp函數的重構應用到sort函數里面呢?
從下面可以看出sort模板有三個參數:
void sort ( RandomAccessIterator first, RandomAccessIterator last ); void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);//排序區間為[first,last) //注: 隨機迭代器,能用此算法的容器是支持隨機訪問的容器:vector, deque, string。不支持鏈表一類的排序。
然后我們轉到sort的定義找到默認的comp:
// TEMPLATE FUNCTION sort template<class _RanIt> inline void sort(_RanIt _First, _RanIt _Last) { // order [_First, _Last), using operator< _STD sort(_First, _Last, less<>()); }
是 less<>(),然后繼續轉到這個的定義去看:
// TEMPLATE STRUCT less template<class _Ty = void> struct less : public binary_function<_Ty, _Ty, bool> { // functor for operator< bool operator()(const _Ty& _Left, const _Ty& _Right) const { // apply operator< to operands return (_Left < _Right); } };
好吧實際上就是個——小於號。
也就是說,比較兩個元素a,b的時候,a<b則comp函數返回true;a>=b則comp函數返回false;由默認排序為升序我們可以推知,comp為true的時候a,b順序不變,!comp為true的時候a,b順序改變。(只是暫時的推理!!后面發現有坑!!)
為了方便記憶和使用,我們可以這么想:記相對位置分別在左邊和右邊的兩個元素(這里不用a和b表示)為left和right,排序后的left和right的關系是使comp返回true的關系。(但要注意一個特殊情況,left = right的時候comp返回都是false,按照排序的原理比較這兩個元素的時候會交換他們的位置,不過交換前后整個數組並無差別,不過這兩個元素只會比較一次,所以也不會無限交換下去(后來事實證明我的想法是錯誤的))
然而事實真的是這樣嗎?
這時候我寫了一個cmp:
1 bool cmp(int left,int right) 2 { 3 return left >= right; 4 }
當在排序的數組中有兩個相同的元素的時候,編譯器報了錯:
於是我就去查了一下問題的原因,然后知道了sort沒有我想的那么簡單。
這里引入一個概念:嚴格弱序(不好意思沒找到定義)下文引用自:https://blog.csdn.net/River_Lethe/article/details/78618788?utm_source=blogxgwz3
什么是嚴格弱序?
C++關聯容器的有序容器對元素關鍵字的類型有要求,元素關鍵字的類型必須定義了嚴格弱序(stick weak ordering)
拿內置類型來說,C++都定義了 “<”操作符,這就是一個嚴格弱序,而“<=”就不是一個嚴格弱序
嚴格弱序有什么用?
對於內置類型我們自然可以有<、>、=來判斷兩個值的大小關系,而對於自定義的類類型,為它定義三種比較操作符是沒有必要的,只用一個嚴格弱序(這里就用<為例)就可以表示兩個元素三種大小關系
-
a小於b
a < b
-
b小於a
b < a
-
a等於b
!(a < b) && !( b < a )
嚴格弱序的三條要求
- 兩個關鍵字不能同時“嚴格弱序”於對方
- 如果a“嚴格弱序”於b,且b“嚴格弱序”於c,則a必須“嚴格弱序”於c
- 如果存在兩個關鍵字,任何一個都不“嚴格弱序”於另一個,則這兩個關鍵字是相等的。
如果我們把<代入上面的要求,代替掉“嚴格弱序”,會發現這三條要求簡直是理所當然的,這是因為<正是嚴格弱序的。反之,如果代入<=就會發現邏輯上的錯誤:
1.兩個關鍵字不能同時“<=”於對方
顯然有a<=b,b<=a,a,b相等時成立
2.如果存在兩個關鍵字,任何一個都不“<=”於另一個,則這兩個關鍵字是相等的。
a不小於等於b,且b也不小於等於a,也就是a>b且b>a,這明顯是一個偽命題
然后是報錯的原因以及解決辦法,參考:
https://www.cnblogs.com/hi3254014978/p/11451901.html
和
https://www.cnblogs.com/RookieSuperman/p/12375563.html
而有時,我們所比較的元素並不僅僅是一個值,還可以是一個結構,一個類的實例,或者排序的策略並不想要升序,這時候就要我們自己重構一下cmp函數了。
二、應用
1、非升序排序(int)
#include <algorithm> #include <iostream> using namespace std; bool cmp(int left,int right) { return left > right; } int main() { int a[7] = { 9, 8, -7, -6, 5, 4,4 }; sort(a, a + 7,cmp); for (int i = 0; i < 7; ++i) { cout<<a[i]<<' '; } system("pause"); return 0; }
2.結構體排序(多級排序)
#include<iostream> #include<algorithm> using namespace std; struct node{ int a; int b; }nodes[5] = { {1,20}, {5,4}, {5,6}, {7,8}, {5,6} }; bool cmp(const node left,const node right)//排序規則:按a 從大到小,若a相等,則按b從大到小 { if (left.a == right.a) { return left.b > right.b; } else { return left.a > right.a; } } int main() { sort(nodes, nodes + 5, cmp); for (int i = 0; i < 5; ++i) { cout << "第" << i + 1 << "個:" << nodes[i].a << "," << nodes[i].b<< endl; } system("pause"); return 0; }
3.類實例排序(和同結構體差不多就不寫了)
·····························
4.多條件排序
分析:
這里排序的第一條件是:若兩個數都在arr2中,則按arr2中的順序,
若兩數之中一個數不在arr2中,在arr2中的數在前面,
若兩個數都不在arr2中,則按大小非降序排列.
ok,然后代碼就很好寫:
class Solution { public: vector<int> relativeSortArray(vector<int>& arr1, vector<int>& arr2) { unordered_map <int,int>cixu; for(int i =0;i<arr2.size();++i){ cixu[arr2[i]]=i; } sort(arr1.begin(),arr1.end(),[&](int &a,int &b){ if(cixu.count(a)){ if(cixu.count(b)) return cixu[a]<cixu[b]; else
return true; }else if(cixu.count(b)) return false; return a<b; }); return arr1; } };
//好吧我承認這個匿名函數寫的不漂亮...
未完待續,遇到有意思的我會再加上去