AcWing 787. 归并排序
归并排序代码模板如下:
/*
归并排序
时间O(nlogn),空间O(n)
*/
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int[] q=new int[n];
for(int i=0;i<n;i++)q[i]=sc.nextInt();
//进入归并排序
mergeSort(q,0,n-1);
//打印排序后的数组q
for(int i=0;i<n;i++){
if(i==n-1)System.out.print(q[i]);
else System.out.print(q[i]+" ");
}
}
//归并排序
public static void mergeSort(int[] q,int l,int r){
if(l>=r)return;
int mid=l+r>>1;
//以中间点划分归并区间,先进入递归
mergeSort(q,l,mid);
mergeSort(q,mid+1,r);
//tmp数组暂存[l,r]区间内的排序后的元素
int[] tmp=new int[r-l+1];
//k用于tmp数组赋值,i作为左区间遍历指针,j作为右区间遍历指针
int k=0,i=l,j=mid+1;
//当左右区间当前遍历到的元素有一方更小时,将更小的赋值给tmp数组存储
while(i<=mid&&j<=r){
if(q[i]<q[j])tmp[k++]=q[i++];//同时tmp数组指针和赋值的那个区间指针前进1
else tmp[k++]=q[j++];
}
//可能左右区间有一区间指针先到达右端点,要将剩下还没赋值的元素给tmp数组
while(i<=mid)tmp[k++]=q[i++];
while(j<=r)tmp[k++]=q[j++];
//i用来遍历q数组[l,r]之间的元素进行赋值覆盖
//j用来遍历tmp数组,把排序后的元素给q数组
for(i=l,j=0;i<=r;i++,j++)q[i]=tmp[j];
}
}
AcWing 788. 逆序对的数量
这题可以直接利用归并排序算法的模板,只需要在合并区间排序代码处,累加逆序对数量即可。
此外还需要注意的是n的最大值可能取到10 0000,所以最终逆序对最大可能是n(n-1)/2,超过int的表示范围,res需要定义为long型
代码如下:
import java.util.*;
class Main{
public static void main(String[] args){
Scanner sc =new Scanner(System.in);
int n=sc.nextInt();
int[] q=new int[n];
for(int i=0;i<n;i++)q[i]=sc.nextInt();
System.out.print(mergeSort(q,0,n-1));
}
//有long型返回值res
public static long mergeSort(int[] q,int l,int r){
//long型需要返回值,单个数不成对,返回0
if(l>=r)return 0;
int mid=l+r>>1;
//最坏情况res=(n-1)+(n-2)+...+1=n(n-1)/2约等于10^10/2=5*10^9
//超出int范围,所以要用long类型的res来接
long res=mergeSort(q,l,mid)+mergeSort(q,mid+1,r);
int[] tmp=new int[r-l+1];
int k=0,i=l,j=mid+1;
while(i<=mid&&j<=r){
if(q[i]<=q[j])tmp[k++]=q[i++];
else{//q[i]>q[j]时,q[i~mid]都会大于q[j]
res+=mid-i+1;
tmp[k++]=q[j++];
}
}
while(i<=mid)tmp[k++]=q[i++];
while(j<=r)tmp[k++]=q[j++];
for(i=l,j=0;i<=r;i++,j++)q[i]=tmp[j];
return res;
}
}