一、题目
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
限制:
0 <= 数组长度 <= 50000
二、解决
1、归并排序
思路:
附下归并排序模板:
public static void mergeSort(int[] array, int left, int right) {
if (right <= left) return;
int mid = (left + right) >> 1; // (left+right)/2
mergeSort(array, left, mid);
mergeSort(array, mid+1, right);
merge(array, left, mid, right);
}
public static void merge(int[] arr, int left, int mid, int right) {
int[] temp = new int[right-left+1]; // 中间数组
int i = left, j = mid+1, k = 0;
while (i<=mid && j<=right) {
temp[k++] = arr[i]<=arr[j] ? arr[i++] : arr[j++];
}
while (i<=mid) temp[k++] = arr[i++];
while (j<=right) temp[k++] = arr[j++];
for (int p=0; p<temp.length; p++) {
arr[left+p] = temp[p];
}
// 也可以用 System.arraycopy(sourceArr, sourceStartIndex, destArr, destStartIndex, copyLength);
}
代码:
class Solution {
int res = 0;
public int reversePairs(int[] nums) {
divide(nums, 0, nums.length-1);
return res;
}
public void divide(int[] nums, int left, int right) {
if(left >= right ) return;
int mid = left + ((right - left) >> 1);
divide(nums, left, mid);
divide(nums, mid+1, right);
merge(nums, left, mid, right);
}
public void merge(int[] nums, int left, int mid, int right) {
//合并两个数组 merge(left,mid,right);
int i = left, j = mid + 1, k = 0;
int[] temp = new int[right-left+1];
while(i <= mid && j <= right) {
if ( nums[i] <= nums[j]) {
temp[k++] = nums[i++];
} else {
temp[k++] = nums[j++];
/*
nums[i] > nums[j],则 nums[i]后面的数都大于nums[j]
在这里的时候要统计上,否则nums[j]被合并到有序数组中去了,后面不会再被比较到
*/
res = res + (mid - i + 1);
}
}
while(i <= mid) temp[k++] = nums[i++];
while(j <= right) temp[k++] = nums[j++];
for(int p = 0; p < temp.length; p++) {
nums[left+p] = temp[p];
}
}
}
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
空间复杂度: O ( n ) O(n) O(n),临时数组【temp】空间占用。
2、其他版本
思路: 可看参考—【LeetCode】493. 翻转对,然后进行改写.
代码: 略。
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
空间复杂度: O ( n ) O(n) O(n)