还是一道经典的力扣题目:
https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/
俺是非科班出生的菜鸡,一大把年纪了,第一次写这个😭😭😭😭
熟悉归并排序的话,可以看下后边的分析直接写,如果写不出来,再copy代码
先上代码:
from typing import List
def merge(array, s1, e1, s2, e2):
array_new = array.copy()
# 规定从小到大
CI = 0 # 初始化逆序数
steps = (e1-s1+1) + (e2-s2+1)
start = s1
for i in range(steps):
if s1 > e1:
# 意思是左边的没了, (左边的本来应该小, 先没), 不存在逆序
array_new[start+i] = array[s2]
s2 += 1
continue
if s2 > e2:
# 意思是右边的没了, (右边的本来应该大, 但是没了), 存在逆序
array_new[start+i] = array[s1]
s1 += 1
continue
if array[s1] > array[s2]:
array_new[start+i] = array[s2]
s2 += 1
CI += e1-s1+1 # 如果左边比右边大, 存在逆序
elif array[s1] <= array[s2]:
array_new[start+i] = array[s1]
s1 += 1
# 不要以为 (无需改变原数组, 故而注释)
array[start:e2+1] = array_new[start:e2+1]
return CI
def mergeSort(array, start, end):
# 规定从小到大排序
CI = 0 # 数组中的逆序数, (实际上不用初始化)
if len(array) == 0:
return 0
if end-start == 1:
# 处理长度为 2 的情况
if array[start] > array[end]:
# 不能改变原数组结构了, 故而注释掉
array[start] += array[end]
array[end] = array[start] - array[end]
array[start] = array[start] - array[end]
return 1
return 0
if end == start:
# 处理长度为 1 的情况
return 0
mid = int((start + end) / 2)
L_CT = mergeSort(array, start, mid) # 左半部分逆序数
R_CT = mergeSort(array, mid+1, end) # 右半部分逆序数
LR_CT = merge(array, start, mid, mid+1, end) # 左右两部分逆序数对比
return CI + L_CT + R_CT + LR_CT
class Solution:
def reversePairs(self, nums: List[int]) -> int:
length = len(nums)
import numpy as np
nums = np.array(nums)
return mergeSort(nums, 0, length-1)
IC = Solution.reversePairs(None, [7,5,6,4])
print("逆序数:", IC)
建议先看上一篇博客:
[每日一水] 归并排序 Python实现
在数组求逆序对中,即使用了 Divide and Conquer
,算法复杂度依然是
O
(
n
2
)
O(n^2)
O(n2),为啥呢,因为在后期合并这一步的时候,依然是两层for循环,所以比较慢
所以依旧存在 比较大小的冗余,所以为了去除冗余,则 要千方百计引入结构,好喜欢卜老师的字啊:
也就是在归并排序两部分 merge 的时候,如果右半部分的某些元素要先于左半部分的某些元素插入时,则左半部分比该元素大的个数就是逆序数(废话),而如果左右两部分都存在从小到大的结构时,上述的那个个数就是:
左半部分序列长度 减去 左半部分的id [注意是否有加一减一的动作(id从0开始则不需要加减1)]
(该Slider截图参考自卜东波老师的课件)
如果你想不通为啥在递归调用 mergeSort 的时候,仔细看看上边儿那个图
B站也有卜老师的课
https://www.bilibili.com/video/BV1MK4y1W7Gm
感谢 卜老师