题目描述
牛客网:JZ35 数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
示例
输入:[1,2,3,4,5,6,7,0]
输出:7
题目解析
方法一:暴力破解
根据题意,前面数字大于后面数字即为一个逆序对,两层遍历数组可以得到所有逆序对。
注意:当数据量较大时,int数据不足以表示所有结果,因此计算时使用long型整数。
public int InversePairs(int [] array) {
long p = 0;
for(int i = 0;i < array.length;i++){
for(int j = i + 1;j < array.length;j++){
if(array[i] > array[j]){
p++;
}
}
}
return (int)(p % 1000000007);
}
- 时间复杂度为:O(n2)
对于105 的数据来说可以运行成功,但数据量再大就容易超时。 - 空间复杂度为:O(1)
方法二:归并排序
解法参考:官方题解
-
归并排序思想
归并排序的思想是将数组分为相等的两个子数组,分别进行排序后再进行合并。
通过递归方法将子数组划分至长度为1,最终完成整个数组的排序和合并。
时间复杂度为:O(nlog2n)
空间复杂度为:O(n) -
逆序对求解
考虑这种情况:[4,5,3]和[2,1]
两个子数组要进行排序后合并,则区间之间的逆序对为:- 排序前:(4,2)(4,1)(5,2)(5,1)(3,2)(3,1)
- 排序后区间为[3,4,5]和[1,2],逆序对结果不变
对于当左侧区间的首元素大于右侧区间某元素时,左侧区间的其他元素也大该元素。如
3 > 1
,则有4 > 1
和5 > 1
。因此,对于排序后的子区间来说,若左侧区间首元素有一个逆序对,则左侧区间其他元素也都有了一个逆序对。根据此思路对归并排序算法进行改进,在排序过程中得到逆序对数量。
public int InversePairs(int [] array) {
int ans = 0;
// 将逆序对作为参数加入到排序过程中
ans = mergeSort(array,0,array.length - 1,ans);
return ans;
}
public int mergeSort(int[] array,int left,int right,int ans){
// 区间只有一个数,不用排序,直接返回
if(left >= right){
return ans;
}
// 将区间分为两个子区间
int mid = left + ((right - left) >> 1);
ans = mergeSort(array,left,mid,ans);
ans = mergeSort(array,mid + 1,right,ans);
// 合并两个子区间
ans = merge(array,left,mid,right,ans);
return ans;
}
public int merge(int[] array,int left,int mid,int right,int ans){
// 临时数组,存储排序合并后的子区间
int[] temp = new int[right - left + 1];
int i = left,j = mid + 1,k = 0;
while(i <= mid && j <= right){
if(array[i] > array[j]){
temp[k++] = array[j++];
// 左侧区间大于右边的数
ans += (mid - i + 1);
ans %= 1000000007;
}else{
temp[k++] = array[i++];
}
}
while(i <= mid){
temp[k++] = array[i++];
}
while(j <= right){
temp[k++] = array[j++];
}
for(int m = 0,n = left;n <= right;n++,m++){
array[n] = temp[m];
}
return ans;
}
- 时间复杂度为:O(nlog2n)
- 空间复杂度为:O(n)