题目
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4] 输出: 5
解法一 暴力法 【超时】
用两个for loop 将整个数组两两比较
class Solution {
public int reversePairs(int[] nums) {
if (nums.length < 2) return 0;
int res = 0;
for(int i = 1;i < nums.length;i++){
for(int j = 0; j < i;j++){
if(nums[i] < nums[j]){
res++;
}
}
}
return res;
}
}
时间复杂度:O(n^2)
空间复杂度:O(1)
解法二 归并排序
public class Solution {
public int reversePairs(int[] nums) {
int len = nums.length;
if(len < 2) return 0;
int[] copy = new int[len];
for(int i = 0; i < len; i++){
copy[i] = nums[i];
}
int[] temp = new int[len];
return reverse(copy,0,len-1,temp);
}
public int reverse(int[] nums, int left, int right, int[] temp){
if(left == right) return 0;
int mid = left + (right-left)/2;
// 向下recur左半边
int leftPairs = reverse(nums,left,mid,temp);
// 向下recur右半边
int rightPairs = reverse(nums,mid+1,right,temp);
// 如果已经有序,则不存在会出现在左边和右边的逆序对,直接计算返回
if(nums[mid] <= nums[mid+1]) return leftPairs + rightPairs;
// 计算合并时左右两个数组内互存的逆序对
int crossPairs = mergeAndCount(nums,left,mid,right,temp);
return leftPairs + rightPairs + crossPairs;
}
public int mergeAndCount(int[] nums, int left, int mid, int right, int[] temp){
for(int i = left; i <= right;i++){
temp[i] = nums[i];
}
int i = left;
int j = mid+1;
int count = 0;
for(int k = left;k <= right;k++){
// 如果左边的指针已经超出,则只计算右边的数组
if(i == mid+1){
nums[k] = temp[j];
j++;
// 如果右边的指针已经超出,则只计算左边的数组
}else if(j == right+1){
nums[k] = temp[i];
i++;
// 现在的指针指向的两个数字没有形成逆序对,左边的指针进行挪动
}else if(temp[i] <= temp[j]){
nums[k] = temp[i];
i++;
// 现在的指针指向的两个数字形成逆序对,右边的指针进行挪动
}else{
nums[k] = temp[j];
j++;
// 现在所有左边的还没加进去的数字,都能和右边指针现在指向的数字形成逆序对
count += (mid-i+1);
}
}
return count;
}
}
时间复杂度:O(nlogn),归并排序的复杂度均为这个
空间复杂度:O(n),运用了额外的数组来储存