归并排序是一种稳定且高效的排序算法,在最好,最坏和平均情况下时间复杂度都是nlogn。该算法采用了分而治之的策略,将整个数组逐渐细分(二分的方法)直到每个小的数组只有一个元素,所以它一定是有序的,然后再逐层合并小数组得到有序的原数组。而每层合并的复杂度为O(n)。
本算法只有在合并时的代码多(也即是本算法的主要内容),但是合并两个有序数组并不难,我们可以再开一个临时的数组来辅助(这里是temp数组)。接下来看代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#define maxn 500005
using namespace std;
int n;
int a[maxn],temp[maxn];
void merge(int a[],int l,int mid,int r){
int i=l;
int j=mid+1;
int t=0;
while(i<=mid&&j<=r){
if(a[i]<=a[j])temp[t++]=a[i++];
else temp[t++]=a[j++];
}
while(i<=mid){
temp[t++]=a[i++];
}
while(j<=r){
temp[t++]=a[j++];
}
for(int i=l;i<=r;i++)a[i]=temp[i-l];//最后将temp数组中的值再赋给a数组
}
void mergesort(int a[],int l,int r){
if(l>=r)return;//l=r就表示只有一个元素了
int mid=(l+r)>>1;
mergesort(a,l,mid);
mergesort(a,mid+1,r);
merge(a,l,mid,r);
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
mergesort(a,1,n);
for(int i=1;i<=n;i++){
if(i>1)cout<<" ";
cout<<a[i];
}
return 0;
}
另外归并排序的一个常用的地方是来求一组数中的逆序对数。因为归并排序左右两部分合并时,假设I在左边,j在右边,则左边比j大的元素个数=mid-i+1。这个可以很容易理解(左边最大的是mid,以及判断到了I,此时比j大的肯定是mid-i+1)所以求逆序对数时只需加一句。
while(i<=mid&&j<=r){
if(a[i]<=a[j])temp[t++]=a[i++];
else {
temp[t++]=a[j++];
ans+=mid-i+1;
}
}
大多数我们都可以使用STL中的sort函数来完成排序,复杂度为nlogn,实际上sort函数是一种改进的快速排序,是一种不稳定的排序。另外STL中还有一个stable_sort(稳定的排序)。stable_sort 和 sort的区别在于 前者作排序可以使原来的"相同"的值在序列中的相对位置不变
如 1 4 6 7 4’ (4 和 4’值相等,加上’ 表示是2个元素)
那么stable_sort能保证排序完 4 仍然在4’ 前 也就是输出1 4 4’ 6 7;但是sort 没有这个功能,算法不能保证这一点。
有的题出得很精妙,如果你用sort或stable_sort函数的话都会TIE。像洛谷的P1309,题目链接:https://www.luogu.com.cn/problem/P1309
我们通过分析题意可以这样想,我们为每轮比赛都另建两个数组win[]和lose[],我们可以知道win数组和lose数组内部一定是有序的。所以我们只需要每轮比赛完后合并这两个数组即可,时间复杂度为O(n)。这里用到的就是归并排序的核心思想。
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,r,q;
struct ti{
int s,w,id;
void operator =(ti x){
s=x.s ;w=x.w ;id=x.id ;
}
}sum[200010];
bool cmp(ti x,ti y){
if(x.s !=y.s)return x.s >y.s ;
return x.id <y.id ;
}
struct ti win[200010],lose[200010];
void merge(ti win[],ti lose[]){
int i=1;
int j=1;
int t=1;
while(i<=n&&j<=n){
if(win[i].s>lose[j].s)sum[t++]=win[i++];
else if(win[i].s==lose[j].s){
if(win[i].id<lose[j].id) sum[t++]=win[i++];
else sum[t++]=lose[j++];
}
else sum[t++]=lose[j++];
}
while(i<=n){
sum[t++]=win[i++];
}
while(j<=n){
sum[t++]=lose[j++];
}
}
int main(){
cin>>n>>r>>q;
int N=2*n;
for(int i=1;i<=N;i++){
scanf("%d",&sum[i].s );
}
for(int i=1;i<=N;i++){
scanf("%d",&sum[i].w );
sum[i].id =i;
}
sort(sum+1,sum+N+1,cmp);//第一次排序可以使用sort来排。
for(int i=1;i<=r;i++){
int t1=1,t2=1;
for(int j=1;j<N;j+=2){
if(sum[j].w >sum[j+1].w ){
sum[j].s ++;
win[t1++]=sum[j];
lose[t2++]=sum[j+1];
}
else {
sum[j+1].s++;
win[t1++]=sum[j+1];
lose[t2++]=sum[j];
}
}
merge(win,lose);//每次比完后合并两数组即可
}
cout<<sum[q].id <<endl;
return 0;
}