【算法】acwing788 逆序对的数量(模板题)
给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。 逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<j 且 a[i]>a[j],则其为一个逆序对;否则不是。 输入格式 第一行包含整数 n,表示数列的长度。 第二行包含 n 个整数,表示整个数列。 输出格式 输出一个整数,表示逆序对的个数。 数据范围 1≤n≤100000, 数列中的元素的取值范围 [1,109]。 输入样例: 6 2 3 4 5 6 1 输出样例: 5
此题时归并排序的变题,回顾一手归并code
import java.util.*;
public class Main{
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int [] a = new int [n];
for(int i = 0; i < n; i ++){
a[i] = sc.nextInt();
}
mergeSort(a,0,n-1);
for(int i = 0; i < n; i ++)System.out.print(a[i] + " ");
}
public static void mergeSort(int [] a, int l, int r){
if(l >= r)return;
int x = l + r >> 1;
mergeSort(a,l,x);
mergeSort(a,x+1,r);
int [] t = new int [r-l+1];
int i = l, j = x + 1, k = 0;//双指针
while(i <= x && j <= r ){
if(a[i] <= a[j]){
t[k ++] = a[i ++];
}else{
t[k ++] = a[j ++];
}
}
while(i <= x)t[k++] = a[i++];
while(j <= r)t[k++] = a[j++];
for(int y = l,z = 0; y <= r; y++, z++)a[y]=t[z];
}
}本题思路:利用分治思想,再归并时统计逆序对数量。
我们将序列从中间分开,将逆序对分成三类:
- 两个元素都在左边;
2两个元素都在右边;
两个元素一个在左一个在右;
因此这就是我们算法的大致框架:
计算逆序对的数量(序列):
- 递归算左边的;
- 递归算右边的;
- 算一个左一个右的;
- 把他们加到到一起。
这个时候我们注意到一个很重要的性质,左右半边的元素在各自任意调换顺序,是不影响第三步计数的,因此我们可以数完就给它排序。这么做的好处在于,如果序列是有序的,会让第三步计数很容易。
如果无序暴力数的话这一步是O(n^2)的。
ac coed
import java.io.*;
public class Main{
static int N = 100010;
static int[] a = new int[N];
public static void main(String[] args)throws IOException{
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(bf.readLine());
String[] arrStr = bf.readLine().split(" ");
for (int i = 0; i < n; i++)a[i] = Integer.parseInt(arrStr[i]);
System.out.println(mergeSort(0,n - 1));
bf.close();
}
public static long mergeSort(int l,int r){
if(l >= r)return 0;
int mid = l + r >> 1;
long res = 0;
//情况一和情况二,对左右数组排序
res += mergeSort(l,mid) + mergeSort(mid + 1,r);
//归并过程
int[] tmp = new int[r - l + 1];
int i = l,j = mid + 1, k = 0;
while(i <= mid && j <= r){
if(a[i] <= a[j])tmp[k ++] = a[i ++];
else{
tmp[k ++] = a[j ++];
//情况三
res += mid - i + 1;
}
}
//扫尾过程
while(i <= mid)tmp[k ++] = a[i ++];
while(j <= r)tmp[k ++] = a[j ++];
//物归原主
for(int x = l,y = 0; x <= r; x ++ ,y ++)a[x] = tmp[y];
return res;
}
}时间复杂度o(nlogn)
