排序

$$ \texttt{SeekLuna 2023.6.11} $$ 论 $\text{beyoursven}$ 有多恐怖这件事……

排序的一些性质

通过比较操作来给同类元素排序。

可排序性

要求比较规满足可排序性:

任意两个待排序的元素都可以进行比较

比较规则满足传递性

例如,石头剪刀布不满足可排序性。这谁都知道

稳定性

排序后相等元素的位置是否发生变化

如果一定不发生变化,那么我们称它为稳定的排序

如果可能发生变化,那我们称它为不稳定的排序

逆序对

处于逆序关系的一对元素:存在下标 $i,j,(i < j)$,满足 $a_{i} > a_{j}$,称 $(i,j)$ 为一对逆序对

例题:逆序对统计

题目让我们求出 $n$ 个不重复的数中有多少个逆序对

既然这题数据够小,那我们就可以……手打暴力

示例Code:

#include<iostream>

using namespace std;

const int MAXN = 1e3 + 10;

int n,ret,a[MAXN];

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++){
      ret += a[i] > a[j] ? 1 : 0;
    }
  }
  cout << ret;
  return 0;
}

排序函数

C++ STL(Standard Library,标准库)中通过 <algorithm> 头文件中的函数 sort()stable_sort() 预先实现了一些排序算法,只不过,sort()不稳定,而stable_sort()是稳定的(可能后者用的是 $\text{Merge Sort}$ 吧……)。

用法:函数名(起始地址,结束地址,比较器);,表示按比较器函数实现的排序规则对(起始地址,结束地址)做排序,其中比较器是可选参数

sort()stable_sort() 都是基于比较的排序,平均需要 $n \cdot \log(n)$ 次的元素比较和移动,其中 $n$ 表示待排序元素的数量。基于比较的排序时间复杂度至少为 $O(n\log(n))$。

例题:数字排序1

题目让我们从小到大排序,我们就可以用sort函数啦~

示例Code:

#include<iostream>

using namespace std;

const int MAXN = 1e5 + 10;

int n,a[MAXN];

int main(){
  cin >> n;
  for (int i = 1; i <= n; i++){
    cin >> a[i];
  }
  stable_sort(a + 1,a + n + 1);
  for (int i = 1; i <= n; i++){
    cout << a[i] <<' ';
  }
  return 0;
}

比较器

用函数实现的自定义比较规则。

在$C++$中,比较器接受两个同类型参数,第一个参数靠前时返回$true$,否则返回$false$(包括相等的情况)

例如:大数靠前的比较器:

bool cmp(int i,int j){

    return i > j;
}

例题:数字排序2

题目让我们从大到小排序,我们又可以用sort函数啦~(

当然我们需要一个cmp函数

示例Code:

#include<iostream>

using namespace std;

const int MAXN = 1e5 + 10;

int n,a[MAXN];

bool cmp(int x,int y){
  return x > y;
}

int main(){
  cin >> n;
  for (int i = 1; i <= n; i++){
    cin >> a[i];
  }
  stable_sort(a + 1,a + n + 1,cmp);
  for (int i = 1; i <= n; i++){
    cout << a[i] << ' ';
  }
  return 0;
}

结构体排序!

重点终于来了(

结构体排序是指开一个结构体,这知道吧,在写一个cmp函数,这也知道吧,然后修改一下cmp,把里面的return语句改一下,假设是这样的:

const int MAXN = 1e5 + 10;

struct Student{
    int c,id;
}a[MAXN];

可见这是一个结构体,确切地说,是一个用来存储学生的某某某东西的结构体,然后我们要对它进行排序,怎么排呢?如果你要排的是 $c$(默认小数靠前),那么cmp就这么写:

bool cmp(const Student &a, const Student &b){
    return a.c < b.c;
} 

然后如果你要排的是$id$(还是小数靠前),就这么写:

bool cmp(const Student &a, const Student &b){
    return a.id < b.id;
}

大的靠前的同理

例题1:排序与编号

题目让我们给长度为 $n$ 的数组排序,然后输出排序后的编号,那么我们可以这样,先写一个结构体,里面有$c,id$两个值,还有一个用来存储数据的$Node$类型的$a$数组:

const int MAXN - 1e5 + 10;

struct Node{
    int c,id;
}a[MAXN];

然后,就是刚刚讲的结构体排序,因为题目给的是让我们从小到大排序

bool cmp(const Node a, const Node b){
    return a.c < b.c;
}

注意:这里要写的是$a.c$,而不是$a.id$,因为不是给$id$排序。

完整示例Code:

#include<iostream>

using namespace std;

const int MAXN = 1e5 + 10;

int n;

struct node{
  int id,c;
}a[MAXN];

bool cmp(node x,node &y){
  return x.c < y.c;
}

int main(){
  cin >> n;
  for (int i = 1; i <= n; i++){
    cin >> a[i].c;
    a[i].id = i;
  }
  sort(a + 1, a + n + 1,cmp);
  for (int i = 1; i <= n; i++){
    cout << a[i].id << ' ';
  }
  return 0;
}

例题2:多关键字排序

题目让我们给同学们的$m$课成绩排序,并按顺序输出排序后的学号,我们还是写结构体排序,注意是有$m$科,所以结构体里面还需要一个数组。

小 a:结构体里是开了数组,但 cmp 函数怎么写?

小 b:谁说cmp函数不能写循环的捏?

$\text{beyoursven}$:典型的只会写板子代码……

示例Code:

#include<bits/stdc++.h>

using namespace std;

const int MAXN = 1e4 + 10,maxn = 1e1 + 10;

int n,m;

struct Student{

    int km[maxn];
}stu[MAXN];

bool cmp(Student &a,Student &b){
    for (int i = 1; i <= m; i++){
        if (a.km[i] != b.km[i]){
            return a.km[i] > b.km[i];
        }
    }
    return false;
}

int main(){
    cin >> n >> m;
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= m; j++){
            cin >> stu[i].km[j];
        }
    }
    sort(stu + 1,stu + n + 1,cmp);
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= m; j++){
            cout << stu[i].km[j] << ' ';
        }
        cout << '\n';
    }
    return 0;
}

重载<运算符

排序函数默认使用 < 符号来比较。通过在结构体内重载 < 运算符,可以让排序函数支持结构体排序的比较规则,类似于比较器。

格式:

bool operator < (const Node & i) const{
    //...
}
//Node为结构体类型名称,i为对象名
//在大括号中实现比较规则,const表示该运算符不能对成员进行修改操作

例题:区间排序

题目让我们按照左边界和右边界排序,这里分为两种情况:

1,左边界不同

2,左边界相同

这两个东西要写一个||符号,左边界相同和另一个条件(右边界不同)要写&&

示例Code:

#include<iostream>

using namespace std;

const int MAXN = 1e5 + 10;

int n;

struct node{
    int l,r;
    bool operator < (const node &k) const {
        return l < k.l ||l == k.l && r < k.r;
    }
}a[MAXN];

int main(){
    cin >> n;
    for (int i = 1; i <= n; i++){
        cin >> a[i].l >> a[i].r;
    }
    sort(a + 1, a + n + 1);
    for (int i = 1; i <= n; i++){
        cout << a[i].l << ' ' << a[i].r << '\n';
    }
    return 0;
}
posted @ 2023-06-12 21:26  rksm2333  阅读(13)  评论(0)    收藏  举报  来源