快排
基本原理
快速排序的核心思想是通过一个分区操作,将待排序的数组分为两部分,其中一部分的所有元素都比另一部分的所有元素小,然后再分别对这两部分继续进行快速排序,整个过程递归进行,直到整个数组有序。不稳定排序
算法步骤 选择基准值,分区操作,递归排序
时间复杂度: 平均情况: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;
}
练习题:
归并排序
基本原理
归并排序(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;
}
练习题:
多关键字排序
多关键字排序,就是对多个维度的数据进行排序,按照一个关键字排序不够,需要按多个关键字依次比较。
对于该例题常见的做法有两种:
第一种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;