归并排序题目-逆序对的数量

给定一个长度为 n的整数数列,请你计算数列中的逆序对的数量。

逆序对的定义如下:对于数列的第 i个和第 j个元素,如果满足 ia[j],则其为一个逆序对;否则不是。

输入格式

第一行包含整数 n,表示数列的长度。

第二行包含 n个整数,表示整个数列。

输出格式

输出一个整数,表示逆序对的个数。

数据范围

1≤n≤100000
数列中的元素的取值范围 [1,109]。

输入样例:

6
2 3 4 5 6 1

输出样例:

5

求逆序对数量其实就是在求在两部分有序的数列中,来相互比较两个数列的大小,若第一个数列中有值大于第二个数列的值,便会产生逆序对;因此可以使用归并排序算法的思想来解决;

首先根据归并算法的特点,通过不断被递归,最终一个数列中只含有一个数字,因此是有序的,接着便会来到倒数第二层递归,此时一个数列中含有两个数字,相当于一个数列分为两个数列,对这两个数列的值进行比较,如果符合逆序对的规则,则res加一,通过不断的递归返回,直到最后一层递归返回(也就是第一次递归调用的地方),此时是两个大数列,数列内部数量为n/2;这两个大数列与之前小的数列合并为较大的数列是一样的,都是在数列内是有序的,但数列与数列之间并不是有序的,所以每次递归返回其实都是在比较数列与数列之间的值的大小。

import java.util.*;
public class P788 {
	static int N = 100010;
	static int[] arr = new int[N];
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();
		
		for(int i = 0;i < n;i++) {
			arr[i] = scan.nextInt();
		}
		System.out.println(merge_sort(0,n-1));
	}
	public static long merge_sort(int l,int r) {
		if(l>=r) return 0;
		int mid = l + r >> 1;
		long res = merge_sort(l,mid) + merge_sort(mid +1,r);
		int k = 0;
		int[] temp = new int[r-l+1];
		int i= l,j=mid + 1;
		while(i<= mid && j<= r ) {
			if(arr[i] <= arr[j]) temp[k++] = arr[i++];
			else {
				temp[k++] = arr[j++];
				res+=mid-i+1;
			}
		}
		while(i<= mid) {
			temp[k++] = arr[i++];
		}
		while(j <= r) {
			temp[k++] = arr[j++];
		}
		for( i = l,k = 0;i <= r;i++,k++) arr[i] = temp[k];
		
		return res;
	}

}

这里注意一点,因为题目中的数据范围最大是100000,一般对于数据范围大于10w的题目就要考虑数据溢出、时间复杂度等问题。
那么对于最大100000规模的数据,逆序对最多的数量为多少呢?
当数列是一个倒序的数列时应该是逆序对数量最大的时候,每一个数可以和他后面所有的数形成逆数对,如果有n个数,那么总的逆序对数量为:(n-1)+(n-2)+……+1即n(n−1)/2个,大约n22
个,代入10w的数据范围得最多逆数对个数为:5×109,这个数据大于int的范围。所以这个题用int的话会溢出,我们采用long来存结果。

你可能感兴趣的:(蓝桥杯,算法,数据结构)