【C艹】關於sort用法之重構cmp(comp)函數的筆記


  

  眾所周知,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 )

  

嚴格弱序的三條要求

  1. 兩個關鍵字不能同時“嚴格弱序”於對方
  2. 如果a“嚴格弱序”於b,且b“嚴格弱序”於c,則a必須“嚴格弱序”於c
  3. 如果存在兩個關鍵字,任何一個都不“嚴格弱序”於另一個,則這兩個關鍵字是相等的。

 

 

  如果我們把<代入上面的要求,代替掉“嚴格弱序”,會發現這三條要求簡直是理所當然的,這是因為<正是嚴格弱序的。反之,如果代入<=就會發現邏輯上的錯誤:

 

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; } };

//好吧我承認這個匿名函數寫的不漂亮...

未完待續,遇到有意思的我會再加上去

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM