ACM入门之【排序】

188 阅读4分钟

快排

基本原理

快速排序的核心思想是通过一个分区操作,将待排序的数组分为两部分,其中一部分的所有元素都比另一部分的所有元素小,然后再分别对这两部分继续进行快速排序,整个过程递归进行,直到整个数组有序。不稳定排序

算法步骤 选择基准值,分区操作,递归排序

时间复杂度: 平均情况:O(nlogn) 最坏情况:O(n²)

板子代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],n;
void f(int l,int r)
{
    if(l>=r) return;
    int i=l-1,j=r+1,x=a[l+r>>1];
    while(i<j)
    {
        do i++;while(a[i]<x);
        do j--;while(a[j]>x);
        if(i<j) swap(a[i],a[j]);
    }
    f(l,j),f(j+1,r);
}
int main(void)
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    f(0,n-1);
    for(int i=0;i<n;i++) cout<<a[i]<<" ";
    return 0;
}

练习题:

快排板子题

786. 第k个数

归并排序

基本原理

归并排序(Merge Sort)是一种经典的排序算法,它基于分治法的思想,将一个大问题分解为多个小问题来解决。归并排序的核心思想是将数组分成两半,分别对这两半进行排序,然后再将排序好的两半合并成一个有序数组。稳定排序

算法步骤

1. 分解
  • 如果数组的长度小于等于1,直接返回,因为一个元素的数组或空数组本身就是有序的。
  • 否则,找到数组的中间位置,将数组分成左右两半。
  • 对左半部分和右半部分分别递归调用归并排序。
2. 合并
  • 定义两个指针,分别指向左半部分和右半部分的起始位置。
  • 比较两个指针所指向的元素,将较小的元素放入结果数组中,并将对应的指针向右移动一位。
  • 重复上述过程,直到其中一个子数组的所有元素都被放入结果数组。
  • 将另一个子数组中剩余的元素直接复制到结果数组的末尾。

时间复杂度:O(n log n)

板子代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5*5+10;
int a[N],b[N],n;
void merge_sort(int l,int r)
{
    if(l>=r) return;
    int mid=(l+r)/2;
    merge_sort(l,mid),merge_sort(mid+1,r);
    int i=l,j=mid+1,k=0;
    while(i<=mid&&j<=r)
    {
        if(a[i]<=a[j]) b[k++]=a[i++];
        else b[k++]=a[j++];
    }
    while(i<=mid) b[k++]=a[i++];
    while(j<=r) b[k++]=a[j++];
    for(int i=l,j=0;i<=r;i++,j++) a[i]=b[j];
}
int main(void)
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    merge_sort(0,n-1);
    for(int i=0;i<n;i++) cout<<a[i]<<" ";
    return 0;
}

归并排序常见的扩展应用便是求逆序对板子代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long int LL;
int a[N],b[N],n;
LL ans;
void merge_sort(int l,int r)
{
    if(l>=r) return;
    int mid=l+r>>1;
    merge_sort(l,mid),merge_sort(mid+1,r);
    int i=l,j=mid+1,k=0;
    while(i<=mid&&j<=r)
    {
        if(a[i]<=a[j]) b[k++]=a[i++];
        else b[k++]=a[j++],ans+=mid-i+1;
    }
    while(i<=mid) b[k++]=a[i++];
    while(j<=r) b[k++]=a[j++];
    for(int i=l,j=0;i<=r;i++,j++) a[i]=b[j];
}
int main(void)
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    merge_sort(0,n-1);
    cout<<ans;
    return 0;
}

练习题:

787. 归并排序

788. 逆序对的数量

多关键字排序

多关键字排序,就是对多个维度的数据进行排序,按照一个关键字排序不够,需要按多个关键字依次比较。

P1093 [NOIP 2007 普及组] 奖学金

对于该例题常见的做法有两种:

第一种cmp函数的写法:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct node
{
    int a,b,c,id,sum;
}Node[N];
int n;
bool cmp(node a,node b)
{
    if(a.sum != b.sum) return a.sum>b.sum;
    if(a.a!=b.a) return a.a>b.a;
    return a.id<b.id;
}
int main(void)
{
    cin>>n;
    for(int i=0;i<n;i++) 
    {
        cin>>Node[i].a>>Node[i].b>>Node[i].c;
        Node[i].id=i,Node[i].sum=Node[i].a+Node[i].b+Node[i].c;
    }
    sort(Node,Node+n,cmp);
    for(int i=0;i<min(n,5);i++) cout<<Node[i].id+1<<" "<<Node[i].sum<<'\n';
    return 0;
}

第二种重载的写法:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Student {
    int id;
    int chinese, math, english;
    int total;

    bool operator < (const Student& other) const {
        if (total != other.total) return total > other.total;
        if (chinese != other.chinese) return chinese > other.chinese;
        return id < other.id;
    }
};

int main() {
    int n;
    cin >> n;
    vector<Student> students(n);

    for (int i = 0; i < n; ++i) {
        int c, m, e;
        cin >> c >> m >> e;
        students[i] = {i + 1, c, m, e, c + m + e}; // 学号从1开始
    }

    sort(students.begin(), students.end());

    for (int i = 0; i < 5 && i < n; ++i) {
        cout << students[i].id << " " << students[i].total << endl;
    }

    return 0;
}

cmp函数的拓展使用:

priority_queue<int> maxHeap;                        // 默认大根堆
priority_queue<int, vector<int>, greater<int>> minHeap; // 小根堆


自定义大根堆
struct Student {
    int id, total;
};

// 仿函数写法(注意这是结构体)
struct cmp {
    bool operator()(const Student& a, const Student& b) const {
        return a.total < b.total; // 大根堆:总分高的优先
    }
};

priority_queue<Student, vector<Student>, cmp> pq;


自定义set
struct Student {
    int id, total;
};

struct cmp {
    bool operator()(const Student& a, const Student& b) const {
        if (a.total != b.total) return a.total > b.total; // 降序排列
        return a.id < b.id; // 总分相同时按学号
    }
};

// 用法
set<Student, cmp> s;