# 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。 # 输入: [7,5,6,4] # 输出: 5 # 限制: # 0 <= 数组长度 <= 50000
# 1、显然,可以使用O(n^2)复杂度的暴力解法 # 但是50000的数据大小,显然是不行的 # 2、逆序对问题的求解,和归并息息相关,归并的主要思想是将两个数组分开, # 将问题转换成两个有序序列的合并问题,合并过程是将左右序列更小那个放入新序列 # 当右边最小数比左边最小数更小时,意味着左边序列的所有数,可以和当前右边最小数形成逆序对
首先看看基础的归并
def mergeSort(arr):
if len(arr) < 2:
return arr
# mid = math.floor(len(arr) / 2)
mid = len(arr) // 2
# 注意裁剪时,mid不需要+1 arr[:mid]是arr[mid]之前的,
# arr[mid:]是包括arr[mid]和它之后
l, r = arr[0: mid], arr[mid:]
return merge(mergeSort(l), mergeSort(r))
def merge1(l, r):
res = []
while l and r:
if l[0] <= r[0]:
# 此处不能使用pop,因为应当删去list第一个元素
# 使用deque传参会变成list,因此用裁剪解决
res.append(l[0])
l = l[1:]
else:
res.append(r[0])
r = r[1:]
if l:
res += l
if r:
res += r
return res
解题
# 以下是我的写法,虽然的确能跑,但50k数据量还是超时了
# (大概是裁剪有严重效率问题,裁剪一次复制一次,hhh)
class Solution:
count = 0 # 注意类的全局变量需在此处定义
def reversePairs(self, nums: List[int]) -> int:
self.count = 0
self.mergeSort(nums)
return self.count
def mergeSort(self, arr):
if len(arr) < 2:
return arr
mid = len(arr) // 2
l, r = arr[:mid], arr[mid:]
return self.merge(self.mergeSort(l), self.mergeSort(r))
def merge(self, l, r):
res = []
while l and r:
if l[0] <= r[0]:
res.append(l[0])
l = l[1:]
else:
res.append(r[0])
r = r[1:]
if l:
res += l
elif r:
res += r
return res
这玩意跑是能跑,但是跑50000数据量超时了,因为裁剪去掉list首元素,py3裁剪操作是复制操作,于是把merge函数改成使用下标操作
def merge(self, l, r):
res = []
i, j = 0, 0
l_l, r_l = len(l), len(r)
while i < l_l and j < r_l:
if l[i] <= r[j]:
res.append(l[i])
i += 1
else:
self.count += l_l - i # 长度减下标,不需要+1
res.append(r[j])
j += 1
if i < l_l:
res += l[i:]
elif j < r_l:
res += r[j:]
return res
顺利通过
对比一下大佬的写法
# 大佬的写法
class Solution1:
def reversePairs(self, nums: List[int]) -> int:
# 与之前的写法不同的是,这里l r是边界的上下标
def mergeSort(l: int, r: int) -> int:
if l >= r:
return 0
mid = (l + r) // 2
res = mergeSort(l, mid) + mergeSort(mid + 1, r)
i, j = l, mid + 1
tmp[l: r + 1] = nums[l: r + 1] # 重复利用tmp进行复制
for k in range(l, r + 1): # 因为nums[r]也要参与运算,所以此处r+1
if i == mid + 1: # k表示的是排序后数组的下标,通过tmp比较,直接复制回nums
# 左边已遍历完
nums[k] = tmp[j]
j += 1
elif j == r + 1 or tmp[i] <= tmp[j]:
# 右边已遍历完或左边比较小
nums[k] = tmp[i]
i += 1
else:
# 右边比较大
nums[k] = tmp[j]
j += 1
res += mid - i + 1 # 下标相减,是需要+1的
return res
tmp = [0] * len(nums)
return mergeSort(0, len(nums) - 1)
大佬写法重复利用tmp,似乎是更优的写法,但是提交后看统计是我的写法更少内存和时间,可能是我拼接剩下的list是直接+=,大佬一个个复制,所以更好