求逆序对的两种方法
1.归并排序
直接暴力查找时间复杂度为O(n2),但是如果查找第i个数和第1至i-1个数组成的逆序对,只要第1至i-1个数有序,所以只需要找到第一个比i大的数k,则第k到i-1都能与第i个数构成逆序对
所以我们可以在归并排序过程中查找
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Arrays;
class Main{
static long res = 0;
public static void main(String[] args) throws IOException {
StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
cin.nextToken();
int n = (int)cin.nval;
int[] nums = new int[n];
for(int i=0;i<n;i++){
cin.nextToken();
nums[i] = (int)cin.nval;
}
merge_sort(nums,0,n-1);
System.out.println(res);
}
public static void merge_sort(int[] nums,int l,int r){
if(l>=r)
return;
int mid = (l+r)>>1;
merge_sort(nums,l,mid);
merge_sort(nums,mid+1,r);
merge(nums,l,mid,r);
}
public static void merge(int[] nums,int l,int mid,int r){
int[] arrl = Arrays.copyOfRange(nums,l,mid+1);
int[] arrr = Arrays.copyOfRange(nums,mid+1,r+1);
int i =0,j = 0;
int k =l;
while(i<arrl.length&&j<arrr.length){
if(arrl[i]<=arrr[j]){
nums[k++] = arrl[i++];
}
else{
nums[k++] = arrr[j++];
res+= mid-l-i+1;
}
}
while(i<arrl.length){
nums[k++] = arrl[i++];
}
while(j<arrr.length){
nums[k++] = arrr[j++];
}
}
}
2.树状数组
对于数字x,我们在插入时记录他的插入次数i,所以我们在求以这个数为第二个数组成的逆序对的数量 = i - query(x),表示在这个数之前插入且大于这个数的数目
但是如果x过大,就会造成溢出,所以我们可以使用离散化处理
import java.util.Set;
import java.util.TreeSet;
public class lsh {
public static int lower_bound(int[] arr,int x){ //在数组中找到第一个大于等于x的下标
int low = 0,high = arr.length-1;
int res = 0;
while(low<=high){
int mid = (low+high)>>1;
if(arr[mid]>=x){
res = mid;
high = mid-1;
}
else
low = mid+1;
}
return res;
}
public static void solve(int[] nums){
Set<Integer> set = new TreeSet<>();
for(int i:nums)
set.add(i);
int[] unique = new int[set.size()];
int k = 0;
for(int i:set)
unique[k++] = i;
for(int i=0;i<nums.length;i++){
nums[i] = lower_bound(unique,nums[i])+1;
}
}
}
以下就是离散化后的树状数组代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Set;
import java.util.TreeSet;
class Main{
static int[] nums;
static int[] tree;
static int n;
public static void main(String[] args) throws IOException {
StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
cin.nextToken();
n = (int)cin.nval;
nums = new int[n];
tree = new int[n+1];
long cnt = 0;
for(int i=0;i<n;i++) {
cin.nextToken();
nums[i] = (int) cin.nval;
}
solve(nums); //离散化
for(int i=0;i<n;i++) {
update(nums[i]);
cnt += i-query(nums[i])+1; //在这个数前面插入且大于这个数总数 = 总插入数 - 在这个数前面插入且小于这个数总数 -1
}
System.out.println(cnt);
}
public static void update(int index){
for(int i=index;i<=n;i+=(i&(-i))){
tree[i]++;
}
}
public static long query(int x){
long sum=0;
for(int i=x;i>0;i-=(i&(-i)))
sum+=tree[i];
return sum;
}
public static int lower_bound(int[] arr,int x){ //在数组中找到第一个大于等于x的下标
int low = 0,high = arr.length-1;
int res = 0;
while(low<=high){
int mid = (low+high)>>1;
if(arr[mid]>=x){
res = mid;
high = mid-1;
}
else
low = mid+1;
}
return res;
}
public static void solve(int[] nums){
Set<Integer> set = new TreeSet<>();
for(int i:nums)
set.add(i);
int[] unique = new int[set.size()];
int k = 0;
for(int i:set)
unique[k++] = i;
for(int i=0;i<nums.length;i++){
nums[i] = lower_bound(unique,nums[i])+1;
}
}
}