「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」
题目
剑指 Offer 51. 数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
解法一
思路
暴力,就是凎。
当然啦,结局是过不了,时间超限制了。但也是一种思路嘛
/**
* @param {number[]} nums
* @return {number}
*/
var reversePairs = function(nums) {
let len = nums.length;
let count = 0;
for(let i=0;i<len-1;i++){
for(let j=i+1;j<len;j++){
if(nums[i]>nums[j]){
count++;
}
}
}
return count
};
复杂度分析
时间复杂度:O(n2)
空间复杂度:O(1)
解法二
思路:
归并排序。
还记得我们归并排序是怎么做的么,不记得可以戳这里我不信你看不懂我这篇归并排序
那么我先举个例子,如果去结合归并排序计算逆序对
假设有两个已经排序好的数组arr1 = [2,5,8,9],arr2 = [3,6,7,10]
然后我们有个空数组M = [ ],这个空数组用来装arr2里比较后较小的数(怎么比较我们后面说,莫慌)
我们这样来统计逆序对
- 用arr1里第一个数2,去和arr2里第一数3比较,2更小,将2从arr1中拿出不要了。这时候arr1 = [5,8,9],arr2 = [3,6,7,10],M = [ ];
- 继续用arr1里第一个数5,去和arr2里第一数3比较,显然3小,将3拿出放入M中。这时候arr1 = [5,8,9],arr2 = [6,7,10],M = [ 3];
- 继续用arr1里第一个数5,去和arr2里第一数6比较,这时候5更小,将5拿出不要了。这时候注意了,M中的数肯定都比5小,那么5是不是可以和M中所有的数都构成逆序对?此时M中只有一个3,所以构成1个逆序对,逆序对数量+1。这时候arr1 = [8,9],arr2 = [6,7,10],M = [ 3];
- 继续用arr1里第一个数8,去和arr2里第一数6比较,这时候6更小,将6拿出放入M中。这时候arr1 = [8,9],arr2 = [7,10],M = [ 3,6];
- 继续用arr1里第一个数8,去和arr2里第一数7比较,这时候7更小,将7拿出放入M中。这时候arr1 = [8,9],arr2 = [10],M = [ 3,6,7];
- 继续用arr1里第一个数8,去和arr2里第一数10比较,这时候8更小,将8拿出不要了。这时候M中的数肯定都比8小,那么8是不是可以和M中所有的数都构成逆序对?此时M中有3,6,7,所以构成3个逆序对,逆序对数量+1。这时候arr1 = [9],arr2 = [10],M = [ 3,6,7];
那么规律是不是已经找到了
每从左边数组拿出一个数,就去看看之前右边数组拿出了几个数,有几个就可以构成几个逆序对。
那么这个规律可以很好的和我们归并排序结合起来:
- 每次做合并操作的时候,如果从左边的数组拿出数放入result的时候,就去看一下右边数组此时拿出了几个数,有几个数就把逆序对+ 几。
- 那我们其实只要在归并排序的代码上稍微修改一下,加上一个数量统计即可。
再看看我们归并排序的图,用上面的规律去算一下合并过程的逆序对
我已经把合并过程中的逆序对统计,用小数字标记了,总共是7个逆序对
代码如下:
/**
* @param {number[]} nums
* @return {number}
*/
var reversePairs = function(nums) {
let len = nums.length;
let result = [];
result.count = 0;
dfs(nums, result, 0, len-1)
return result.count;
};
function dfs(nums, result, start, end) {
if(start >= end) return;
let block = end - start + 1;
let mid = Math.ceil(block/2) + start - 1;
// all in [start,mid] is left
// all in [mid+1,end] is right
let start1 = start;
let end1 = mid;
let start2 = mid + 1;
let end2 = end;
dfs(nums, result, start1, end1);
dfs(nums, result, start2, end2);
let k = start;
let useBlock2 = 0;
while(start1 <= end1 && start2 <= end2){
if(nums[start1] <= nums[start2]){
result[k] = nums[start1];
result.count += useBlock2;
start1++;
k++
} else {
result[k] = nums[start2];
useBlock2++;
start2++;
k++;
}
}
while(start1 <= end1){
result[k] = nums[start1];
result.count += useBlock2;
start1++;
k++
}
while(start2 <= end2){
result[k] = nums[start2];
start2++;
k++
}
for(let i=start;i<=end;i++){
nums[i] = result[i];
}
}
复杂度分析
时间复杂度:O(nlogn),同归并排序
空间复杂度:O(n),同归并排序