题目
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例1
输入: [7,5,6,4]
输出: 5
题解
暴力求解
双重循环,找到所有可能的逆序对;思路没问题,但是50000的数据量,肯定是超时的。
代码
var reversePairs = function(nums) {
const len = nums.length;
let result = 0;
for(let i = 0 ; i < len ; i++){
for(let j = i+1 ; j < len ; j++){
if(nums[i] > nums[j])result++;
}
}
return result
};
利用归并排序
先写个归并排序,然后改几行代码就可以;不难信我。
- 归并排序的时候使用分治思想,总将数组分为左右两部分
- 对于任意数组list,数组分为左半部分为left,右半部分为right;
- 在递归划分数组的时候,使划分后的数组有序,什么时候数组有序呢?数组长度小于2的时候有序;
- 当left与right有序的时候,开始合并left与right
- 在合并的过程中计算【逆序】
- 如何计算呢?
假设现在有数组left = [5],right = [3];
left合并right过程中是不是[3,5],合并过程中,先放3,后放5;在放3的时候;left还有几个数?还有1个,则存在逆序对1个;
在假设:现在有数组left = [5,7],right = [3,6];
合并left和right,先放3,left长度为2;逆袭对+2; 因为3与5、7都构成逆序对
在放5,5逆序对不增加;
在放6,放6的时候left长度为1,逆序对+1;因为6与7构成逆序对对吧
所以在归并排序的合并上,只要right[r]中的值大于left[l]表示的值,此时right[r]的值可以与left构成(left.length - l)个逆序对。
根据上述思路,编辑代码如下:
代码
var reversePairs = function (nums) {
let result = 0
mergeSort(nums)
return result
function mergeSort(nums) {
//获取数组长度
const len = nums.length
//如果数组长度小于2返回数组
if (len < 2) return nums
// 否则分割数组
const mid = len >> 1 //右移1位,类似除以2
// 递归分解左侧数组
const left = mergeSort(nums.slice(0, mid))
// 递归分解右侧数组
const right = mergeSort(nums.slice(mid, len))
// 合并左右两个数组
return merge(left, right)
// 合并数组
}
function merge(left, right) {
let l = 0
let r = 0
let list = []
while (l < left.length && r < right.length) {
if (left[l] <= right[r]) {
list.push(left[l])
l++
} else {
list.push(right[r])
result += left.length - l
r++
}
}
// 这两行代码省去了两个判断
list = list.concat(left.slice(l, left.length))
list = list.concat(right.slice(r, right.length))
return list
}
}