初等排序 讲义

这里我们简述一下初等排序的学习内容:

  • 选择排序
  • 冒泡排序
  • 比较函数
  • 结构体函数

选择排序

(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 的比较函数,他里面有两个参数 ab ,这个函数返回 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++高级知识点了,目前的阶段是好好掌握比较函数。

posted @ 2019-09-02 23:38  zifeiynoip  阅读(288)  评论(0编辑  收藏  举报