归并排序
归并排序是一种利用分治思想和递归方式实现的一种稳定的排序算法,时间复杂度O(nlog n),主要包含分解、合并两步
分解过程:
将当前序列分解成(l,mid)和(mid+1,r)两个序列,直到不能再分解(即序列长度为1)为止
合并过程:
合并指的是在递归回溯时将两个有序序列合并成一个由这两个序列中的元素组成的有序序列。
具体实现:
首先需要一个空序列用于临时存储合并结果,然后需要两个指针指向两个序列的起始位置,由于合并的两个序列一定相邻,所以一开始指针i=l,指针j=mid+1。
然后比较a[i]和a[j]的大小,将值小的元素放进合并序列末尾,元素对应的指针后移一位,循环至其中一个序列中所有元素都放进了合并序列中
按顺序复制另一个序列的剩余元素到合并序列
最后将合并序列的结果复制回a数组
代码实现:
#include<iostream>
using namespace std;
const int N=1e5+10;
int n;
int a[N],t[N];//a为原数组,t为临时存储合并结果数组
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=l;
while(i<=mid&&j<=r)
{
if(a[i]<=a[j]){
t[k++]=a[i++];
}else{
t[k++]=a[j++];
}
}
//将剩余元素复制到合并序列
//因为不知道哪个序列用完了,所以都要写
while(i<=mid){
t[k++]=a[i++];
}
while(j<=r){
t[k++]=a[j++];
}
//t复制回a
for(int p=l;p<=r;p++){
a[p]=t[p];
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
merge_sort(1,n);
for(int i=1;i<=n;i++){
cout<<a[i]<<" ";
}
return 0;
}
归并排序求逆序对
逆序对:
在一个有序集合中,有一对正整数i,j满足a[i]>a[j]且1<=i<j<=n,则<a[i],a[j]>这个有序对称为这个有序集合的一个逆序对
原理:
在合并过程中,比较a[i]和a[j]的值,如果a[j](右边序列中的数)小于a[i],那么a[j]和当前左边序列剩余的所有元素都构成逆序关系,并且每次参与合并的右边序列都不会和之前的右边序列重复,所以统计的逆序对也不会重复。综上,归并排序可以统计一个有序集合中逆序对的个数,输出所有的逆序对也是同理,将统计换成输出即可
代码实现:
#include<iostream>
using namespace std;
const int N=1e5+10;
int n,ans;
int a[N],t[N];//a为原数组,t为临时存储合并结果数组
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=l;
while(i<=mid&&j<=r)
{
if(a[i]<=a[j]){
t[k++]=a[i++];
}else{
t[k++]=a[j++];
//统计逆序对的个数,仅需要加一步
ans+=mid-i+1;
}
}
//将剩余元素复制到合并序列
//因为不知道哪个序列用完了,所以都要写
while(i<=mid){
t[k++]=a[i++];
}
while(j<=r){
t[k++]=a[j++];
}
//t复制回a
for(int p=l;p<=r;p++){
a[p]=t[p];
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
merge_sort(1,n);
cout<<ans;
return 0;
}
参考书籍:
《信息学奥赛一本通(C++)第五版》