剑指 Offer 51. 数组中的逆序对
原始题目链接:https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
解题思路:
统计逆序对,想到可以使用归并排序,在排序过程中比较大小,再统计逆序对的个数,归并是个分和治的过程,分是一分为二,一直达到每个子数组元素只有1个元素,合的过程是比较大小再选择排序的过程。
代码实现:
class Solution:
def reversePairs(self, nums: List[int]) -> int:
# 归并排序:分 治,在治的阶段统计逆序对的个数
def merge_sort(left, right):
# 递归结束条件,左边界大于等于右边界
# 需要返回个返回值,因为是利用归并排序统计逆序对的个数
if left >= right:
return 0
# 分:计算中间值
mid = left + (right - left) // 2
# 将数组分成两半,继续分,一直分到数组元素只有1个
# 所以继续递归调用
res = merge_sort(left, mid) + merge_sort(mid + 1, right)
# 治:排序的过程中,统计逆序对的个数
# 使用两个指针,i,j,分别指向要归并的两个相邻数组的首元素,用于比较大小
i, j = left, mid + 1
# 申请一个数组复制待比较的两个相邻数组(要进行合并的 左数组 和 右数组)的元素
# 用于i,j去索引数组元素比较大小
temp[left : right + 1] = nums[left : right + 1]
for k in range(left, right + 1):
# 如果左数组的指针达到末尾,即左数组元素都合并且比较完成
# 那么将有数组中的元素直接复制到存储的已排序结果数组中
# mid是左侧数组的最后一个元素索引,达到mid + 1,表示超出左侧数组,元素都遍历完成
if i == mid + 1:
nums[k] = temp[j]
j += 1
# 如果右侧数组都遍历结束,并且左侧数组元素小于等于右侧数组元素
# 那么将左侧数组元素直接复制到存储的已排序结果数组中
elif j == right + 1 or temp[i] <= temp[j]:
nums[k] = temp[i]
i += 1
# 左侧数组元素大于右侧数组元素的情况
# 这种情况符合逆序,因为按照升序排列,左侧数组元素要放在右侧后面,但现在在右侧数组的前面
# 逆序对的个数是两个数组的中间索引 - 当前左侧数组元素大于右侧数组元素的这个索引 + 1
# 并且把右侧数组元素复制到结果数组中
else:
nums[k] = temp[j]
j += 1
# 逆序对的个数
res += mid - i + 1
return res
# 初始化temp数组
temp = [0] * len(nums)
return merge_sort(0, len(nums) - 1)
参考文献:
https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/solution/jian-zhi-offer-51-shu-zu-zhong-de-ni-xu-pvn2h/