众所周知,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;
    }
};

//好吧我承认这个匿名函数写的不漂亮...

未完待续,遇到有意思的我会再加上去