首页 > 技术文章 > STL函数库的应用第二弹——快排sort函数与结构体关键字排序

zaza-zt 2020-04-18 01:31 原文

时隔20多天,本蒟蒻终于记起了他的博客园密码!!!

废话不多说,今天主题:STL快排函数sort()与结构体关键字排序

Part 1:引入和导语

首先,我们需要知道,algorithm库里有一些奇怪的函数。

这些函数可以替代一些代码,使你的程序更加简洁好懂,还可以偷懒。

比如在进行DP时的状态转移时可以用的max()和min()可以快速比较两个数的大小,

又或者是abs(),看似没什么用的绝对值函数,

亦或是lower_bound(),upper_bound()拯救二分渣(比如我)的二分查找函数。

……

等等等等,只有你想不到,没有STL库的函数做不到。

我相信有很多刚开始学排序的同学被一些简单的排序题整的死去活来。明明很简单,代码却难的要死,又臭又长,根本就不想打那么复杂的代码!

而且复杂度不够优秀,更dio更优秀的归并(二分法排序)又不会写,怎么办?

Part 2:简单介绍快速排序——sort()函数

今天主要讲sort()快速排序函数,可以上述问题。

它的复杂度O(nlogn)。

这个复杂度就很优秀,比一般的冒泡,选择,插入排序都要优秀的多。

另外,sort()并不只是单纯的快速排序,他的复杂度不会像手写快速排序那样退化,稳定在O(nlogn)

对于桶,只要数据过大桶就不行了,但是sort可以轻松应对(滑稽)

另外,顺带一提,sort()默认从小到大排序,但是聪明的程序猿们不会让自己的函数这么鸡肋,所以赋予了它更强大的功能……那都是后话,一会我会专门提到。

现在请大家看程序,程序同样简洁优秀。

上基本模板代码,由于我直接sort()快速排序,所以是默认从小到大排。

#include<algorithm>
#include<cstdio>
using namespace std;
int a[3];//定义您要排序的数组 
int main()
{
    for(int i=0;i<3;i++)
    {
        scanf("%d",&a[i]);
    }
    sort(a,a+3);//对它进行sort()排序,格式sort(排序数组名字+n,排序数组名字+m,???) 
    //这里的a是排序对象,+n和+m是指从第n项到第m-1项进行排序。???是重载函数位,一会提到。 
    for(int i=0;i<3;i++)
    {
        printf("%d ",a[i]);//再输出 
    }
    return 0;
} 

这样就可以方便的对您的数组进行从小到大排序了,唯一要注意的是开STL库<algorithm>

很简洁对吧?但是同样,也很鸡肋,如果我想从大到小排怎么办?

教您一招!

for(int i=2;i>=0;i--)
{
    printf("%d ",a[i]);
} 

没错您可以倒着输出!!!(逃)

咳咳!说正经的,到底如何实现从大到小排序呢?

请出我们今天的大部头——cmd重载函数!

Part 3:重载您的快排函数!

吼吼吼!妈妈再也不用担心我的代码又臭又长了!但是还有一个问题没有解决——对于强迫症患者,怎么让它从大到小排序呢?(我就想从第一个元素开始循环我不管!!!)

cmd函数可以满足您的需求(我叫它cmd,dalao们可以取别的高端大气的名字QAQ)

具体如何操作?请康康我的代码。

#include<algorithm>
#include<cstdio>
using namespace std;
int a[3];//定义您要排序的数组 
bool cmd(int x,int y)//定义一个高端的重载函数名字,后面是两个变量 。注意是bool类型!
{
    return x>y;//返回x>y,意思是当输入两个元素的时候,大的在前。(这里x>y和x>=y没有本质区别) 
    //反之,如果是<号的话,就是小的在前,但是那样就没意义了。
}
int main()
{
    for(int i=0;i<3;i++)
    {
        scanf("%d",&a[i]);
    }
    sort(a,a+3,cmd);//对它进行sort()排序,格式sort(排序数组名字+n,排序数组名字+m,重载函数名字) 
    //这里的a是排序对象,+n和+m是指从第n项到第m-1项按重载规则进行排序。
    for(int i=0;i<3;i++)
    {
        printf("%d ",a[i]);//再输出 
    }
    return 0;
}

和第一次给出的代码,只是多了一个cmd函数。

这样我们可以实现从大到小排序了。

其实不仅是从大到小排序,我们还可以干更nb的事情——关键字快速排序。来,同志们看下一part。(吸引读者阅读兴趣)

Part 4:关键字快速排序

有很多优秀(caodan)的题通常是这样的:

给出一堆人的成绩,优先按总分由高到低排序,总分一样按数学成绩由高到低排序(看不起语文英语的魂淡!!!),数学成绩一样再按输入顺序从先到后排序。

dalao们应该一眼就看出这是个结构体的题,只要开结构体就可以轻松AC,但是我们探讨的不是结构体,而是,关键字排序。

如果您使用手写冒泡等排序方法,程序必然非常优秀(caodan)

刚才那个题是我自己瞎编的一个“经典”关键字排序,用冒泡的程序如下(我自己写了一遍,真是给自己挖坑)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int chi,mat,eng;
struct STU{
    string st;
    int tot;
    int math;
    int num;
}QAQ[5]; 
int main()
{
    for(int i=0;i<5;i++)
    {
        cin>>QAQ[i].st;
        scanf("%d%d%d",&chi,&mat,&eng);
        QAQ[i].tot=chi+mat+eng;
        QAQ[i].math=mat;
        QAQ[i].num=i;
    }
    for(int i=4;i>=1;i--)
    {
        for(int j=0;j<i;j++)
        {
            if(QAQ[j].tot<QAQ[j+1].tot)
            {
                swap(QAQ[j],QAQ[j+1]);//总分大的交换总分小的 
            }
            if(QAQ[j].tot==QAQ[j+1].tot)//总分一样看数学 
            {
                if(QAQ[j].math<QAQ[j+1].math)
                {
                    swap(QAQ[j],QAQ[j+1]);//数学大的交换数学小的
                }
                if(QAQ[j].math==QAQ[j+1].math)//数学一样看输入顺序
                {
                    if(QAQ[j].num>QAQ[j+1].num)//输入顺序小的交换输入顺序大的 
                    {
                        swap(QAQ[j],QAQ[j+1]);
                    }
                }
            }
        }
    }
    for(int i=0;i<5;i++)
    {
        cout<<QAQ[i].st<<endl;//输出名字
    }
    return 0;
    //欢迎dalao们造数据狙击我,因为没有真题,我不保证程序没有问题!!! 
}

如果不用重载快排,很容易造成如上的if套娃情况。

为了拒绝套娃,让您的代码清爽一些,我们选择sort()重载排序。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int chi,mat,eng;
struct STU{
    string st;
    int tot;
    int math;
    int num;
}QAQ[5];
bool cmp(STU a,STU b)
{
    if(a.tot!=b.tot)
    {
        return a.tot>b.tot;//如果总分不一样,返回较大值 
    }
    else
    {
        if(a.math!=b.math)
        {
            return a.math>b.math;//总分一样,数学不一样,返回数学较大值 
        }
        else
        {
            return a.num<b.num;//数学也一样,返回输入顺序较小值 
        }
    }
} 
int main()
{
    for(int i=0;i<5;i++)
    {
        cin>>QAQ[i].st;
        scanf("%d%d%d",&chi,&mat,&eng);
        QAQ[i].tot=chi+mat+eng;
        QAQ[i].math=mat;
        QAQ[i].num=i;
    }
    sort(QAQ,QAQ+5,cmp);//直接排序
    for(int i=0;i<5;i++)
    {
        cout<<QAQ[i].st<<" ";
    }
    return 0;
}

您看!套娃的次数从5次减少到了2次。(好像没什么大变化啊)

但是,这只是本蒟蒻随便弄的一个题,下面上一个极其简单(caodan)的模拟/排序题。

刚学会的童鞋们建议先不要看,去洛谷找几个简单的关键字排序练习一下,毕竟这个玩意是绿题。

洛谷P1786 :帮贡排序

很显然,这题用手写关键字排序会直接死亡(不排除dalao狙击)

代码下放

//P1786
//#include<zhangtao.std>
#include<algorithm> 
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
char dd[10];
struct STU{
    int num;
    string name;
    int pos;
    long long score;
    int level;
}AK[120];
int n;
bool cmp1(STU a,STU b)
{
    if(a.score!=b.score)
    {
        return a.score>b.score;
    }
    else
    {
        return a.num<b.num;
    }    
}
bool cmp2(STU c,STU d)
{
    if(c.pos!=d.pos)
    {
        return c.pos>d.pos;
    }
    else
    {
        if(c.level!=d.level)
        {
            return c.level>d.level;
        }
        else
        {
            return c.num<d.num;
        }
    }
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        AK[i].num=i;
        cin>>AK[i].name;
        scanf("%s",dd);
        if(dd[0]=='B')
        {
            if(dd[6]=='u')
            {
                AK[i].pos=7;
            }
            else
            {
                AK[i].pos=1;
            }
        }
        if(dd[0]=='F')
        {
            AK[i].pos=6;
        }
        if(dd[0]=='H')
        {
            AK[i].pos=5;
        }
        if(dd[0]=='Z')
        {
            AK[i].pos=4;
        }
        if(dd[0]=='T')
        {
            AK[i].pos=3;
        }
        if(dd[0]=='J')
        {
            AK[i].pos=2;
        }
        cin>>AK[i].score>>AK[i].level;
        if(AK[i].pos==7)
        {
            AK[i].score=1000000007;
        }
        if(AK[i].pos==6)
        {
            AK[i].score=1000000006;
        }
    }
    sort(AK,AK+n,cmp1);
    for(int i=3;i<n;i++)
    {
        if(3<=i&&i<=4)
        {
            AK[i].pos=5;
        }
        if(5<=i&&i<=8)
        {
            AK[i].pos=4;
        }
        if(9<=i&&i<=15)
        {
            AK[i].pos=3;
        }
        if(16<=i&&i<=40)
        {
            AK[i].pos=2;
        }
        if(41<=i)
        {
            AK[i].pos=1;
        }
    }
    sort(AK,AK+n,cmp2);
    for(int i=0;i<n;i++)
    {
        cout<<AK[i].name;
        if(AK[i].pos==7)
        {
            cout<<" BangZhu ";
        }
        if(AK[i].pos==6)
        {
            cout<<" FuBangZhu ";
        }
        if(AK[i].pos==5)
        {
            cout<<" HuFa ";
        }
        if(AK[i].pos==4)
        {
            cout<<" ZhangLao ";
        }
        if(AK[i].pos==3)
        {
            cout<<" TangZhu ";
        }
        if(AK[i].pos==2)
        {
            cout<<" JingYing ";
        }
        if(AK[i].pos==1)
        {
            cout<<" BangZhong ";
        }
        cout<<AK[i].level<<endl;
    }
    return 0;
}

大家自己领会代码,主要思路就是用数字代替帮会中的位置,然后进行重载运算符,然后关键字排序即可。

今天的分享就到这里,再见dalao们!!!

推荐阅读