「这是我参与2022首次更文挑战的第30天,活动详情查看:2022首次更文挑战」
看一百遍美女,美女也不一定是你的。但你刷一百遍算法,知识就是你的了~~
谁能九层台,不用累土起!
题目
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
限制:
0 <= 数组长度 <= 50000
解题思路
按照老套路,我们肯定会尝试一下暴力解。
var reversePairs = function(nums) {
let count = 0
for(let i = 0;i<nums.length;i++){
for(let j = i+1;j<nums.length;j++){
if(nums[j]<nums[i]) count++
}
}
return count
};
但是提交后我们会发现超出了时间限制,也就是说明O(n²)
的时间复杂度不能满足题目。
看来只能换一种思路了。
那我们接下来看看归并操作的妙用。
首先,先简单介绍一下归并操作:
归并操作的工作原理如下:
- 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
- 设定两个指针,最初位置分别为两个已经排序序列的起始位置
- 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
- 重复步骤3直到某一指针超出序列尾
- 将另一序列剩下的所有元素直接复制到合并序列尾.
因此,我们在归并的过程中只要计算每个小区间的逆序对数,进而计算出大区间的逆序对数。
解题代码
var reversePairs = function(nums) {
let arr = new Array(nums.length) // 用数组暂存区间排序结果
function getCount(left,right){
if(left>=right) return 0 // 如果当前区间中元素数量小于等于1 返回0
const mid = (left+right)>>1 // 获取中间索引
let count = 0
count += getCount(left,mid) // 对左右两个区间进行递归获取逆序对数量
count += getCount(mid+1,right)
let index = left
let low = left
let high = mid+1
while(low<=mid||high<=right){
if(high>right || (low<=mid && nums[low]<=nums[high])){
arr[index++] = nums[low++] // 左区间
} else{
arr[index++] = nums[high++] // 元素落在右区间,左区间中未处理的元素都与当前元素组成逆序对
count += mid-low+1
}
}
for(let i = left;i<=right;i++){// 用排序后的数组更新原数组
nums[i] = arr[i]
}
return count
}
return getCount(0,nums.length-1)
};
如有任何问题或建议,欢迎留言讨论!