?
归并排序
基本思想
归并排序是用分治的思想。将大问题分成许多小问题。在每层递归上有三个步骤
具体演示
将数组一分为二,直到分割到只剩一个元素的时候开始向上递归,可以发现在合并数组之前,两个子数组都是有序的
代码模板
#include
using namespace std;
const int N=1e6+10;
int tmp[N];
void merge_sort(int q[],int l,int r){
? ?//递归函数的出口。当切割到只剩一个元素的时候向上递归
? ?if(l>=r)return;
? ?//移位的优先级没有加法高,相当于(l+r)/2
? ? int mid=l+r>>1;
? ?//对左半边处理
? ?merge_sort(q,l,mid);
? ?//对右半边处理
? ?merge_sort(q,mid+1,r);
? ?//处理完成两部分数组 l-mid整体有序 mid+1到r整体有序,通过临时数组tmp对两个数组进行操作。定义一个左指针和右指针来对比元素的大小,k为tmp的长度也就是l-r的元素个数
? ?int i=l,j=mid+1,k=0;
? ?//当两个数组都有剩余元素进行循环如果左边数组小,将左边数组元素放入tmp,指针向后移动一位,数组长度k++,反之数组右边元素放入tmp,指针向后移动一位,数组长度++;
? ?while(i<=mid&&j<=r){
? ? ? ?if(q[i]<q[j])tmp[k++]=q[i++];
? ? ? ?else tmp[k++]=q[j++];
? }
? ?//如果上面的循环结束说明有一个数组已经没有的剩余元素。那么另外的数组的剩余元素的第一个一定大于tmp[k],只需将剩余数组的元素平移到tmp即可
? ?while(i<=mid)tmp[k++]=q[i++];
? ?while(j<=r)tmp[k++]=q[j++];
? ?//对tmp进行遍历。将tmp的元素平移到目标数组的l-r
? ?for(int i=l,j=0;j<k;i++,j++){
? ? ? ?q[i]=tmp[j];
? }
}
int main(){
? ?int q[N],n;
? ?cin>>n;
? ?for(int i=0;i<n;i++)cin>>q[i];
? ?merge_sort(q,0,n-1);
? ?for(int i=0;i<n;i++)cout<<q[i]<<" ";
}
逆序对的数量
给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。 逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<j 且 a[i]>a[j] ,则其为一个逆序对;否则不是。
输入格式
第一行包含整数 n,表示数列的长度。 第二行包含 n 个整数,表示整个数列。
输出格式
输出一个整数,表示逆序对的个数。
数据范围
1
≤
n
≤
100000
,
1≤n≤100000 ,
1≤n≤100000,
数列中的元素的取值范围[1,1e9]
输入样例
6
2 3 4 5 6 1
输出样例
5
题目的意思是数组前面的数比后面的大就能构成一对逆序对[2,1],[3,1],[4,1],[5,1],[6,1]正好五对。可以是用递归的方法处理这个问题。将可能出现的逆序对抽象成三种情况
三种情况组成的逆序对相加就是数组的全部逆序对,对数组的左右两边排序,对第三种条件的计算会方便很多。例如q[L]>q[R],那么q[L]-q[MID]就可以和q[R]组成逆序对。因为两个数组都是有序的。那么q[L]之后的元素一定都大于等于q[L]。在加上q[L]>q[R]所以L后面的元素包括L都可以和下标为R的元素构成逆序对。所以采用归并排序解决。归并的特点正好完全符合这个问题。其实具体拆解,只存在第三种情况,第二种和第一种情况是由第三种情况计算得出的。
算法模板
#include
using namespace std;
const int N=1e6+10;
int tmp[N];
//数据范围过大 使用longlong类型
long long merge_sort(int q[],int l,int r){
? ?if(l>=r) return 0;
? ?int mid=l+r>>1;
? ?//将第三种情况的值转换为第一第二种
? long long ans = merge_sort(q,l,mid)+merge_sort(q,mid+1,r);
? ? long i=l,j=mid+1,k=0;
? ?while(i<=mid&&j<=r){
? ?
? ? ? ?if(q[i]<=q[j])tmp[k++]=q[i++];
? ? ? ?else{
? ? ? ? ? ?tmp[k++]=q[j++];
? ? ? ? ? ?//mid-i+1为i-mid的元素个数,都能与q[j]组成逆序对
? ? ? ? ? ?ans=ans+mid-i+1;
? ? ? }
? }
? ?while(i<=mid)tmp[k++]=q[i++];
? ?while(j<=r) tmp[k++]=q[j++];
? ?// for(int i=0;i<6;i++)cout<<tmp[i]<<" ";
? ?// cout<<endl;
? ?for(int i=l,j=0;j<k;j++,i++){
? ? ?
? ? ? ?q[i]=tmp[j];
}
return ans;
}
int main(){
? ? int q[N],n;
? ?cin>>n;
? ?for(int i=0;i<n;i++)cin>>q[i];
? ?long long k=merge_sort(q,0,n-1);
? ?cout<<k<<endl;
? ? ?
? return 0;
}
?