【题目】
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。
输入一个数组,求出这个数组中的逆序对的总数。
[7,5,6,4] => 5
【归并排序】
// 入口方法
function search(nums = []) {
sort(nums, 0, nums.length - 1)
return nums
}
// 归并排序中间方法,将数组分为两个数组,进行排序,排序完后再merge合在一起
function sort(nums, l, r) {
if (l < r) {
// 计算中间位置
let mid = ((r - l) >> 1) + l
// 左排序
sort(nums, l, mid)
// 右排序
sort(nums, mid + 1, r)
// 左右合并merge
merge(nums, l, mid, r)
}
}
// 比对合并拆分的两个数组
function merge(nums, l, mid, r) {
// 左指针
let i = l
// 右指针
let j = mid + 1
// 临时数组
let result = []
// 指针在正常的数组中遍历
while(i <= mid && j <= r) {
// 当左数组的数值小于等于右数组的数值,将左数组的数值放进result
if (nums[i] <= nums[j]) {
result.push(nums[i])
i++
}
// 当右数组的数值小于左数组的数值,将右数组的数值放进result
else if (nums[i] > nums[j]) {
result.push(nums[j])
j++
}
}
// 因为左/右数组已经走完,则将右/左数组的剩余参数放进result
if (i > mid) {
result.push(...nums.slice(j, r + 1))
}
else if (j > r) {
result.push(...nums.slice(i, mid + 1))
}
// 到这里,result数组中存在的是已经排序好的数组
// 再将排序好的数组放回nums中相对应的位置
const rs = result
nums.splice(l, rs.length, ...rs)
}
【逆序对】
在归并排序的基础上,计算每次数值对比后左边大于后边的次数,注意这里在计算次数的时候要判断左数组剩余数据的次数累加,防止漏加
// 入口方法
function search(nums = []) {
let sum = 0
return sort(nums, 0, nums.length - 1, sum)
}
// 归并排序中间方法,将数组分为两个数组,进行排序,排序完后再merge合在一起
function sort(nums, l, r, sum) {
if (l < r) {
// 计算中间位置
let mid = ((r - l) >> 1) + l
// 左排序
sum = sort(nums, l, mid, sum)
// 右排序
sum = sort(nums, mid + 1, r, sum)
// 左右合并merge
sum = merge(nums, l, mid, r, sum)
}
return sum
}
// 比对合并拆分的两个数组
function merge(nums, l, mid, r, sum) {
// 左指针
let i = l
// 右指针
let j = mid + 1
// 临时数组
let result = []
// 指针在正常的数组中遍历
while(i <= mid && j <= r) {
// 当左数组的数值小于等于右数组的数值,将左数组的数值放进result
if (nums[i] <= nums[j]) {
result.push(nums[i])
i++
}
// 当右数组的数值小于左数组的数值,将右数组的数值放进result
else if (nums[i] > nums[j]) {
// 只有当右数组的数值小于(不等于)左数组的数值的时候,才计算逆序对的次数
// 因为左数组的数值大于右数组,则左数组剩余的所有数都会大于,所以这里加的是左数组从i开始到mid结束的次数
sum = sum + (mid - i + 1)
result.push(nums[j])
j++
}
}
// 因为左/右数组已经走完,则将右/左数组的剩余参数放进result
if (i > mid) {
result.push(...nums.slice(j, r + 1))
}
else if (j > r) {
result.push(...nums.slice(i, mid + 1))
}
// 到这里,result数组中存在的是已经排序好的数组
// 再将排序好的数组放回nums中相对应的位置
const rs = result
nums.splice(l, rs.length, ...rs)
return sum
}
【时间复杂度】
归并排序的时间复杂度为O(n*logn)
空间复杂度因为只使用了一个等长度的数组,所以是O(n)
【参考】
左程云左神的算法课程
一周刷爆LeetCode,算法大神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解_哔哩哔哩_bilibili