描述

题目描述

首先我们是有 T组数据 ,接下来我们每组数据输入一个 n 代表的是我们有 n位同学的成绩需要我们来进行一个排序,接下来输入一个 op 这个代表的就是我们按照什么规则来排序

  1. op == 0 时,代表我们排序要按照 从高到低来排序
  2. op == 1 时,代表我们排序要按照 从低到高来排序 接下来就是 n 组数据,每组数据的 第一个是姓名第二个是成绩

样例解释

3
1
fang 90
yang 50
ning 70
3
0
moolgouua 43
aebjag 87
b 67

首先第一个 3 代表的是代表的是有三组数据,接下来的 1 是让我们把接下来的三个成绩按照从低到高的顺序来进行一个排序,那么我们经过从低到高的排序后,我们可以得到第一个样例输出

yang 50
ning 70
fang 90

然后我们对第二个样例进行一个解释, 3 代表的是有三组数据,接下来 0 代表的是我们把接下来的三组数据按照成绩从高到低排序,排序后我们可以得到这么一个结果

aebjag 87
b 67
moolgouua 43

这道题我们可以看出,我们要做的就是首先把姓名跟成绩对应存储下来,然后进行一个排序,最后我们把我们的思路进行一个化简,发现这个题是一个考察结构体排序的题目

知识点: 结构体排序

难度: 两星

题解:

解题思路:

这道题我们发现了是考察我们的一个结构体排序的知识点,但是这里面有一个需要我们注意的问题: 如果有两位同学的成绩是相同的,那么我们最后输出的顺序要按照他们输入的顺序来输出

对于这道题目来讲,我们存储我们的成绩对应姓名,使用结构体的时候空间复杂度是 O(n) 的,时间复杂度也是 O(n) 的,但是对于我们的排序来讲使用不同的排序的时候,他的时间复杂度是不一样的,假设我们使用最为暴力的双重 for 循环的排序做法,那么我们总体的时间复杂度就是 n2n ^ 2 的,因为一个程序的时间复杂度是取决于他的最高的时间复杂度的

解法一 :暴力解法

我们采用双重 for 循环的方式,然后排序成绩以及他的id,我们的代码实现如下

时空复杂度分析

首先我们开辟了结构体数组用以存储我们的姓名,成绩,以及输入的顺序,这里我们输入的时间复杂度是 O(n) 的,然后空间复杂度是 3*n 的,但是在算法竞赛中,我们前面的常数是忽略的,所以我们这个的空间复杂的是 O(n) 的,然后我们的暴力排序算法经过了 n * (n - 1) / 2 次运算的,然后计算时间复杂度就是n2n ^ 2的时间复杂度,最后这种暴力求解的方法,空间复杂度是O(n) 的,时间复杂度是 n2n ^ 2

#include <bits/stdc++.h>
using namespace std;

const int N = 210;
struct node {
    string name;
    int val, id;
} a[N];

int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int n;
    while(cin >> n) { // 多组输入我们的n组数据
        int op;
        cin >> op; // 输入我们的排序规则
        for (int i = 1; i <= n; i++) {
            cin >> a[i].name >> a[i].val;
            a[i].id = i;
        }
        // 输入我们的 n 组的名字以及成绩,最后把id赋值上去
        if (op == 1) {
            for (int i = 1; i <= n; i++) {
                for (int j = i + 1; j <= n; j++) {
                    if (a[i].val > a[j].val) swap(a[i], a[j]); // 首先我们判断如果我们的成绩前面的比后面的大,那么我们交换
                    else if (a[i].val == a[j].val) {
                        if (a[i].id > a[j].id) swap(a[i], a[j]);
                        // 如果我们的成绩相同,前者的id比后者的id大,我们也要交换
                    }
                }
            }
        } else {
            for (int i = 1; i <= n; i++) {
                for (int j = i + 1; j <= n; j++) {
                    if (a[i].val < a[j].val) swap(a[i], a[j]); // 首先我们判断如果我们的成绩前面的比后面的小,那么我们交换
                    else if (a[i].val == a[j].val) {
                        if (a[i].id > a[j].id) swap(a[i], a[j]);
                        // 如果我们的成绩相同,前者的id比后者的id大,我们也要交换
                    }
                }
            }
            // 这里是如果两者的成绩相同,那么返回id较小的,否则返回成绩大的
        }
        // 我们对两次进行一个排序
        for (int i = 1; i <= n; i++) {
            cout << a[i].name << " " << a[i].val << endl;
        }
        // 输出我们排序后的结果
    }
    return 0;
}

图解算法

alt alt alt alt alt alt

解法二 : 快速排序 - 自定义排序函数

时空复杂度分析

这里我们要追求更快的处理速度,那么我们要采取更快的一个排序的方式,这里我们选择了快速排序,这次我们采用的算法的空间复杂度是O(n) 的,但是我们的时间复杂度使用的是快速排序,快速排序的时间复杂度是O(nlogn) 的,那么我们这种快速排序搭配结构体的算法的总体的时间复杂度就是 O(nlogn) 的,空间复杂度就是O(n) 的,可能会有小伙伴疑问,为什么输入的时间复杂度O(n) 我们在这里面不考虑呢? 因为在我们的算法竞赛中,一个算法的时间复杂度取决于的是它最慢的一个时间,换种理解方式就是,在n很大的时候,我们的小的地方是可以忽略不记的,所以我们时间复杂度取得是一个程序中的瓶颈的时间复杂度

这里我给出两种写法,第一种是自己定义比较函数一般成为 cmp

#include <bits/stdc++.h>
using namespace std;

const int N = 210;
struct node {
    string name;
    int val, id;
} a[N];

bool cmp1(node wa, node wb) {
    if (wa.val == wb.val) return wa.id < wb.id;
    return wa.val < wb.val;
}
// cmp1这里是如果两者的成绩相同,那么返回id较小的,否则返回成绩小的
bool cmp2(node wa, node wb) {
    if (wa.val == wb.val) return wa.id < wb.id;
    return wa.val > wb.val;
}
// cmp2这里是如果两者的成绩相同,那么返回id较小的,否则返回成绩大的
int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int n;
    while(cin >> n) { // 输入有多少组待排序的数据
        int op;
        cin >> op; //  输入排序规则
        for (int i = 1; i <= n; i++) {
            cin >> a[i].name >> a[i].val;
            a[i].id = i;
        }
        // 将我们的姓名和成绩输入,并且记录下来id,就是输入的顺序
        if (op == 1) {
            sort(a + 1, a + n + 1, cmp1);
            // 以cmp1的规则排序
        } else {
            sort(a + 1, a + n + 1, cmp2);
            // 以cmp2的规则排序
        }
        for (int i = 1; i <= n; i++) {
            cout << a[i].name << " " << a[i].val << endl;
        }
    }
    return 0;
}

快速排序 - 匿名函数 (c++11及以后)

第二种就是用到了c++11出现的匿名函数的这个语法

#include <bits/stdc++.h>
using namespace std;

const int N = 210;
struct node {
    string name;
    int val, id;
} a[N];

int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int n;
    while(cin >> n) {
        int op;
        cin >> op;
        for (int i = 1; i <= n; i++) {
            cin >> a[i].name >> a[i].val;
            a[i].id = i;
        }
        if (op == 1) {
            sort(a + 1, a + n + 1, [](node wa, node wb) -> bool {
                if (wa.val == wb.val) return wa.id < wb.id;
                return wa.val < wb.val;
            });
            // 这里是如果两者的成绩相同,那么返回id较小的,否则返回成绩小的
        } else {
            sort(a + 1, a + n + 1, [](node wa, node wb) -> bool {
                if (wa.val == wb.val) return wa.id < wb.id;
                return wa.val > wb.val;
            });
            // 这里是如果两者的成绩相同,那么返回id较小的,否则返回成绩大的
        }
        for (int i = 1; i <= n; i++) {
            cout << a[i].name << " " << a[i].val << endl;
        }
    }
    return 0;
}