C++标准模板库(STL):algorithm头文件的常用函数之sort()函数

sort()—排序函数

顾名思义,sort就是用来排序的函数,它根据具体情形使用不同的排序算法,效率很高。一般来说,不推荐C语言中的qsort函数,原因是qsort用起来比较麻烦,涉及很多指针的操作。而且sort在实现中规避了经典快速排序中可能出现的会导致实际复杂度退化到O(n²)的极端情况

1. 如何使用sort排序

sort函数的使用必须加上头文件“#include<algorithm>”和"using namespace std;",其使用的方式如下:

sort(首元素的地址(必填), 尾元素地址的下一个地址(必填), 比较函数(非必填));

可以看到,sort的参数有三个,其中前两个是必填的,而比较函数则可以根据需要填写,如果不写比较函数,则默认对前面给出的区间进行递增排序。

可以先从示例入手:

 1 #include<stdio.h>
 2 #include<algorithm>
 3 using namespace std;
 4 int main() {
 5     int a[6] = {9, 4, 2, 5, 6, -1};
 6     //将a[0]~a[3]从小到大排序
 7     sort(a, a + 4);
 8     for(int i = 0; i < 6; i++) {
 9         printf("%d ", a[i]);
10     }
11     printf("
");
12     //将a[0]~a[5]从小到大排序
13     sort(a, a + 6);
14     for(int i = 0; i < 6; i++) {
15         printf("%d ", a[i]);
16     }
17     return 0;
18 }

运行之后可以得到下面的结果, 可以试着理解一下(特别注意理解“尾元素地址的下一个地址”)。输出结果:

 

2 4 5 9 5 -1
-1 2 4 5 6 9

 

又如,对double型数组排序

 1 #include<stdio.h>
 2 #include<algorithm>
 3  4 using namespace std;
 5 int main() {
 6     double a[] = {1.4, -2.1, 9};
 7     sort(a, a + 3);
 8     for(int i = 0; i < 3; i++) {
 9         printf("%.1f ", a[i]);
10     }
11     return 0;
12 }

输出结果:

-2.1 1.4 9

再如,对char型数组排序(默认为字典序):

#include<stdio.h>
#include<algorithm>
using namespace std;
int main() {
    char c[] = {'T', 'W', 'A', 'K'};
    sort(c, c + 4);
    for(int i = 0; i < 4; i++) {
        printf("%c ", c[i]);
    }
}

输出结果:

A K T W 

关于sort的排序原理细节咱不讨论,但是应当知道,如果需要对序列进行排序,那么序列中的元素一定要有可比性,因此需要制定排序规则来建立这种可比性。特别是像结构体,本身没有大小关系我,需要人为制定比较的规则。sort的第三个可选参数就是compare函数(一般写作cmp函数),用来实现这个规则

2. 如何实现比较函数

下面介绍对基本数据类型、结构体类型、STL容器进行自定义规则排序时cmp的写法。

(1)基本数据类型数组的排序

若比较函数不填,则默认按照从小到大的顺序排序。下面是对int型数组的排序:

#include<stdio.h>
#include<algorithm>
using namespace std;
int main() {
    int a[5] = {3, 1, 4, 2};
    sort(a, a + 4);
    for(int i = 0; i < 4; i++) {
        printf("%d ", a[i]);
    }
    return 0;
}

输出结果:

1 2 3 4 

如果想要从大到小来排序,则要使用比较函数cmp来“告诉”sort何时要交换元素(让元素的大小笔记哦啊关系反过来)。还是上面那个例子,这里比较的元素是int类型,可以这样写:

#include<stdio.h>
#include<algorithm>
using namespace std;
bool cmp(int a, int b) {
    return a > b;   //可以理解为当a > b 时把a放在b前面
}
int main() {
    int a[] = {3, 1, 4, 2};
    sort(a, a + 4, cmp);
    for(int i = 0; i < 4; i++) {
        printf("%d ", a[i]);    //输出 4 3 2 1 
    }
    return 0;
}

输出结果:

4 3 2 1

这样就可以让数值较大的元素放在前面,也就达到了从大到小排序的要求。

同样的,对double型数组从大到小的代码如下:

#include<stdio.h>
#include<algorithm>
using namespace std;
bool cmp(double a, double b) {
    return a > b;   //同样是a > b
}
int main() {
    double a[] = {1.4, -2.1, 9};
    sort(a, a + 3, cmp);
    for(int i = 0; i < 3; i++) {
        printf("%.1f ", a[i]);
    }
    return 0;
}

输出结果:

9.0 1.4 -2.1

对char型数组从大到小排序的代码如下:

#include<stdio.h>
#include<algorithm>
using namespace std;
bool cmp(char a, char b) {
    return a > b;   //同样是 a > b
}
int main() {
    char c[] = {'T', 'W', 'A', 'K'};
    sort(c, c + 4, cmp);
    for(int i = 0; i < 4; i++) {
        printf("%c", c[i]);
    }
    return 0;
}

输出结果:

WTKA

记忆方法: 如果要把数据从小到大排列,那么就用"<",因为"a<b"就是左小右大;如果要把数据从大到小排列,那么就用">",因为"a>b"就是左大右小。而当不确定或者忘记时,不妨两种都试一下,就会知道该用哪种了。

(2)结构体数组的排序

现在定义了如下的结构体:

 struct node { int x, y; }ssd[10]; 

如果想将ssd数组按照x从大到小排序(即进行一级排序),那么可以这样写cmp函数:

 bool cmp(node a, node b) { return a.x>b.x; } 

实例如下:

#include<stdio.h>
#include<algorithm>
using namespace std;
struct node {
    int x, y;
}ssd[10];
bool cmp(node a, node b) {
    return a.x > b.x;   //按x值从大到小对结构体数组排序
}
int main() {
    ssd[0].x = 2;   //{2, 2}
    ssd[0].y = 2;
    ssd[1].x = 1;   //{1, 3}
    ssd[1].y = 3;
    ssd[2].x = 3;   //{3, 1}
    ssd[2].y = 1;
    sort(ssd, ssd + 3, cmp);    //排序
    for(int i = 0; i < 3; i++) {
        printf("%d %d
", ssd[i].x, ssd[i].y);
    }
    return 0;
}

输出结果:

3 1
2 2
1 3

而如果想先按x从小打到排序,但当x相等的情况下,按照y的大小从小到大来排序(即进行二级排序),那么cmp的写法是:

 bool cmp(node a, node b) { if(a.x != b.x) return a.x > b.x else return a.y < b.y } 

这里的cmp函数首先判断结构体内的x元素是否相等,如果不相等,则直接按照x的大小来排序;否则,比较两个结构体中y的大小,并按y从小到大排序。

示例如下:

#include<stdio.h>
#include<algorithm>
using namespace std;
struct node {
    int x, y;
}ssd[10];
bool cmp(node a, node b) {
    if(a.x != b.x) return a.x > b.x;    //x不等时按x从大到小排序
    else return a.y < b.y;  //x相等时按y从小到大排序
}
int main() {
    ssd[0].x = 2;   //{2, 2}
    ssd[0].y = 2;
    ssd[1].x = 1;   //{1, 3}
    ssd[1].y = 3;
    ssd[2].x = 3;   //{3, 1}
    ssd[2].y = 1;
    sort(ssd, ssd + 3, cmp);    //排序
    for(int i = 0; i < 3; i++) {
        printf("%d %d
", ssd[i].x, ssd[i].y);
    }
    return 0;
}

输出结果:

2 1
2 2
1 3

(3)容器的排序

在STL标准容器中,只有vector、string、deque是可以使用sort的。这是因为像set、map这种容器是用红黑树实现的(了解即可),元素本身有序,故不允许使用sort排序。

#include<stdio.h>
#include<algorithm>
using namespace std;
bool cmp(int a, int b) {    //因为vector中的元素为int型,因此仍然是int的比较
    return a > b;
}
int main() {
    vector<int> vi;
    vi.push_back(3);
    vi.push_back(1);
    vi.push_back(2);
    sort(vi.begin(), vi.end(), cmp);    //对整个vector排序
    for(int i = 0; i < 3; i++) {
        printf("%d ", vi[i]);
    }
    return 0;
}

输出结果:

3 2 1

再来看string的排序:

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
int main() {
    string str[3] = {"bbbb", "cc", "aaa"};
    sort(str, str + 3); //将string型数组按字典序从小到大到输出
    for(int i = 0; i < 3; i++) {
        cout<<str[i]<<endl;
    }
    return 0;
}

上面的代码输出如下:

aaa
bbbb
cc

如果上面这个例子,需要按字符串长度从小到大排序,那么可以这样写:

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
bool cmp(string str1, string str2) {
    return str1.length() < str2.length();   //按string的长度从小到大排序
}
int main() {
    string str[3] = {"bbbb", "cc", "aaa"};
    sort(str, str + 3, cmp);
    for(int i = 0; i < 3; i++) {
        cout<<str[i]<<endl;
    }
    return 0;
}

输出结果:

cc
aaa
bbbb