这里我们简述一下初等排序的学习内容:
- 选择排序
- 冒泡排序
- 比较函数
- 结构体函数
选择排序
(1)基本思想:每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序排在待排序的数列的最前,直到全部待排序的数据元素排完。
(2)排序过程:
假设现在有 (n) 个数,他们分别是 (a_1, a_2, a_3, a_4, ……, a_n) ,我要将它们按照从小到大排序。
- 第 (1) 轮:我要选出最小的数作为 (a_1) ,于是我从 (2) 到 (n) 遍历 (j) ,如果 (a_1 lt a_j) ,则交换 (a_1) 和 (a_j) ,遍历结束后我们保证 (a_1) 是最小的数。
- 第 (2) 轮:我要选出次小的数作为 (a_2) ,于是我从 (3) 到 (n) 遍历 (j) ,如果 (a_2 lt a_j) ,则交换 (a_2) 和 (a_j) ,遍历结束后我们保证 (a_2) 是最次的数。
- 如是循环……
(3)选择排序代码示例:
#include <bits/stdc++.h>
using namespace std;
int n, a[100];
int main() {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i < n; i ++)
for (int j = i+1; j <= n; j ++)
if (a[i] > a[j])
swap(a[i], a[j]);
for (int i = 1; i <= n; i ++) cout << a[i] << " ";
return 0;
}
这样我能保证在 (n-1) 轮比较后,我的数组能够按照从小到大的顺序排列好。这就是选择排序。
看上去有点像排老大——“我想当老大,不服的上来比一比,比过了你就当老大”。然后排完老大排老二,排完老二排老三,如是循环……
选择排序的时间复杂度是 (O(n^2)) 。
冒泡排序
(1)基本思想:以 (n) 个人排队为例,从第 (1) 个开始,一次比较相邻的两个是否 逆序(“逆序”的意思是说:比如要按照元素从小到大排序,而此时相邻的两个元素中前者比后者大,则需要调换这两者的值),若逆序就交换两人,接着第 (2) 个和第 (3) 个比,若逆序就交换两人,接着第 (3) 个和第 (4) 比,若逆序就交换两人,……,直到 (n-1) 和 (n) 比较,经过一轮比较后,则把最高的人排到最后,即将冒泡一样逐步冒到相应的位置。原 (n) 个人的排序就转换成 (n-1) 个人的排序问题。第二轮从第 (1) 个开始,一次比较相邻的两个人是否逆序对,若逆序就交换两人,直到 (n-1) 和 (n) 比较。如此,进行 (n-1) 轮之后,这个数列就会变成有序的数列。
(2)排序过程:
假设现在有 (n) 个数,他们分别是 (a_1, a_2, a_3, a_4, ……, a_n) ,我要将它们按照从小到大排序。
- 第 (1) 轮:我开一个循环变量 (j) 从 (1) 到 (n-1) ,每次比较 (a_j) 和 (a_{j+1}) ,如果 (a_j > a_{j+1}) ,则交换 (a_j) 和 (a_{j+1}) ;
其实我这么循环是有原因的:一开始 (a_1) 和 (a_2) 比较(如果逆序则交换)之后我能够保证 (a_2) 是前 (2) 个数中最大的;然后再拿 (a_2) 和 (a_3) 比较(如果逆序则交换)之后我能够保证 (a_3) 是前 (3) 个数中最大的;……,最后我们拿 (a_{n-1}) 和 (a_n) 比较,其实是拿前 (n-1) 个数中的最大的和 (a_n) 比(如果逆序则交换),那么我们在第 (1) 轮结束的时候 (a_n) 就是我们数列中最大的数,并且它也恰好存储在了 (a_n) 中。 - 第 (2) 轮:我开一个循环变量 (j) 从 (1) 到 (n-2) ,每次比较 (a_j) 和 (a_{j+1}) ,如果 (a_j > a_{j+1}) ,则交换 (a_j) 和 (a_{j+1}) ;
同时,我这么操作一遍之后能够保证 (a_{n-1}) 是我们数列中次大的; - 第 (3) 轮,我开一个循环变量 (j) 从 (1) 到 (n-3) ,每次比较 (a_j) 和 (a_{j+1}) ,如果 (a_j > a_{j+1}) ,则交换 (a_j) 和 (a_{j+1}) ;
- 如是循环……
(3)冒泡排序代码示例:
#include <bits/stdc++.h>
using namespace std;
int n, a[100];
int main() {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i < n; i ++)
for (int j = 1; j <= n-i; j ++)
if (a[j] > a[j+1])
swap(a[j], a[j+1]);
for (int i = 1; i <= n; i ++) cout << a[i] << " ";
return 0;
}
这样我能保证在 (n-1) 轮比较后,我的数组能够按照从小到大的顺序排列好。这就是冒泡排序。
比较函数和sort函数
比较函数用于确定数组中的排序规则,比如给我一个 int a[]
数组,我可以使用 algorithm
库提供给我的 sort 函数给他排序,比如,我调用:
sort(a, a+n);
能够给数组从 a[0]
到 a[n-1]
按从小到大的顺序进行排序。因为 sort
函数就是按照默认从小到大的顺序来给数组中的元素进行排序的。
但是如果我想要使用 sort
函数来给数组中的元素按照从大到小的顺序排序呢?这个是用我们需要自定义一个比较函数,并且将函数名作为 sort
函数的第 (3) 个参数传递进去。
例如,我下面定义了一个名为 cmp
的比较函数,他里面有两个参数 a
和 b
,这个函数返回 true
说明 a
应该排在 b
前面,返回 false
说明 b
应该排在 a
后面:
bool cmp(int a, int b) {
return a > b;
}
然后将这个函数的名字作为参数传递给 sort
函数调用即可:
sort(a, a+n, cmp);
这样就能保证将 a[0]
到 a[n-1]
按照从大到小排序了。
特别的,对于结构体来说,因为结构体不知道怎么比较大小,所以需要你编写比较函数并放到 sort
函数里面去给他进行排序。
说明:其实对于结构体来说,也有办法让它直到怎么比较大小,就是给结构体定义一个小于号( (lt) ),这种方法叫做“运算符重载”,但是运算符重载放在我们普及组初赛相对来说超纲了,所以这里就不过多的介绍“运算符重载”这个C++高级知识点了,目前的阶段是好好掌握比较函数。