剑指Offer 51. 数组中的逆序对
class Solution:
### 1108 暴力遍历(超时)
def reversePairs(self, nums: List[int]) -> int:
count = 0
nums_len = len(nums)
for cur_idx in range(nums_len):
cur_num = nums[cur_idx]
for next_idx in range(cur_idx+1, nums_len):
if cur_num > nums[next_idx]:
count += 1
return count
### 1108 使用归并排序求逆序数(1340 ms,18.2 MB)
def mergeSort(self, nums, tmp, l, r):
if l >= r: # 若当前子数组只有一个元素,则默认其为有序数组,直接返回(0)
return 0
# 归:每次将原数组(尽可能)一分为二,然后对两个子数组分别进行归并排序(分而治之)
mid = (l + r) // 2
inv_cnt = self.mergeSort(nums, tmp, l, mid) + self.mergeSort(nums, tmp, mid+1, r) # 左右两边同时进行(子)归并排序、计算各自(子)归并排序的逆序数,并把它们加起来
# 并:将已经排序好的两个子数组,继续排序成一个顺序的父数组
i, j, pos = l, mid+1, l # 初始化3个指针:i为左子数组L的指针(指向数组开始位置),j为右子数组R的指针(指向数组R开始位置),pos为辅助数组指针
while i <= mid and j <= r: # 当i还未遍历完数组L且j还未遍历数组R时,重复循环
if nums[i] <= nums[j]: # 比较两个子数组中所指向的值的大小,将较小的值存放进辅助数组tmp
tmp[pos] = nums[i]
i += 1
inv_cnt += (j - (mid+1)) # 若较小值在数组L中,则R中位于j之前的值均与L中i指向的值构成逆序数,则统计逆序数
else:
tmp[pos] = nums[j] # 若较小值在数组R中,则不存在逆序数
j += 1
pos += 1
# 检查子数组L中是否存在剩余的元素,若存在则将剩余元素追加进辅助数组tmp,并继续统计逆序数
for k in range(i, mid+1):
tmp[pos] = nums[k]
pos += 1
inv_cnt += (j - (mid+1))
# 检查子数组L中是否存在剩余的元素,若存在则将剩余元素追加进辅助数组tmp,此时不存在逆序数
for k in range(j, r+1):
tmp[pos] = nums[k]
pos += 1
# 将已经排序好的子数组的元素返回至原数组中原来的位置(原地排序,且归并排序是稳定排序)
nums[l: r+1] = tmp[l : r+1]
return inv_cnt
def reversePairs(self, nums: List[int]) -> int:
tmp = [0] * len(nums)
return self.mergeSort(nums, tmp, 0, len(nums)-1)
### 二刷
class Solution:
def reversePairs(self, nums: List[int]) -> int:
def sort(tmp, l, r):
if l >= r: return 0
mid = (l+r) // 2
inv_cnt = sort(tmp, l, mid) + sort(tmp, mid+1, r)
i, j, p = l, mid+1, l # j = mid+1
while i <= mid and j <= r:
if nums[i] <= nums[j]: # 取等!
tmp[p] = nums[i]
i += 1
inv_cnt += j - (mid+1) # j - (mid+1)
else:
tmp[p] = nums[j]
j += 1
p += 1
for k in range(i, mid+1):
tmp[p] = nums[k]
p += 1
inv_cnt += j - (mid+1)
for k in range(j, r+1):
tmp[p] = nums[k]
p += 1
nums[l:r+1] = tmp[l:r+1]
return inv_cnt
l, r = 0, len(nums)-1
tmp = [0] * len(nums) # 与nums长度相同
return sort(tmp, l, r)
- 另附1:归并排序代码
class Solution:
### 1108 归并排序
def mergeSort(self, nums, tmp, l, r):
if l >= r: # 若当前子数组只有一个元素,则默认其为有序数组,直接返回
return
# 归:每次将原数组(尽可能)一分为二,然后对两个子数组分别进行归并排序(分而治之)
mid = (l + r) // 2
self.mergeSort(nums, tmp, l, mid) # 左数组L进行(子)归并排序
self.mergeSort(nums, tmp, mid+1, r) # 右数组R进行(子)归并排序
# 并:将已经排序好的两个子数组,继续排序成一个顺序的父数组
i, j, pos = l, mid+1, l # 初始化3个指针:i为左子数组L的指针(指向数组开始位置),j为右子数组R的指针(指向数组R开始位置),pos为辅助数组指针
while i <= mid and j <= r: # 当i还未遍历完数组L且j还未遍历数组R时,重复循环
if nums[i] <= nums[j]: # 比较两个子数组中所指向的值的大小,将较小的值存放进辅助数组tmp
tmp[pos] = nums[i] # 若较小值在数组L中
i += 1
else:
tmp[pos] = nums[j] # 若较小值在数组R中
j += 1
pos += 1
# 检查子数组L中是否存在剩余的元素,若存在则将剩余元素追加进辅助数组tmp
for k in range(i, mid+1):
tmp[pos] = nums[k]
pos += 1
# 检查子数组L中是否存在剩余的元素,若存在则将剩余元素追加进辅助数组tmp
for k in range(j, r+1):
tmp[pos] = nums[k]
pos += 1
# 将已经排序好的子数组的元素返回至原数组中原来的位置(原地排序,且归并排序是稳定排序)
nums[l: r+1] = tmp[l : r+1]
return
def reversePairs(self, nums: List[int]) -> int:
tmp = [0] * len(nums)
return self.mergeSort(nums, tmp, 0, len(nums)-1)
88. 合并两个有序数组(Easy)
- 双指针 + 从前往后 / 从小到大:时间复杂度O(n+m),空间复杂度O(m)
- 双指针 + 从后往前 / 从大到小:时间复杂度O(n+m),空间复杂度O(1)
class Solution:
### 1108 双指针 + 从前往后 / 从小到大( 40 ms,13.6 MB)
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
nums1_cpy = nums1[:m] # 引入辅助数组来保存nums1中的元素
nums1[:] = [] # 使用[:]来保证变量nums1的地址不变,也即原地变换in-place
ptr1 = ptr2 = 0 # 初始化双指针为0
while ptr1 < m and ptr2 < n: # 顺序进行
if nums1_cpy[ptr1] > nums2[ptr2]: # 由于是从后往前确定位置,所以这里应该是先存放小的一个元素
nums1.append(nums2[ptr2])
ptr2 += 1
else:
nums1.append(nums1_cpy[ptr1])
ptr1 += 1
# 若nums1或nums2中存在剩余元素,则直接追加
if ptr1 < m:
nums1[ptr1+ptr2: ] = nums1_cpy[ptr1: ]
if ptr2 < n:
nums1[ptr1+ptr2: ] = nums2[ptr2: ]
### 1108 双指针 + 从后往前 / 从大到小(52 ms,13.5 MB)
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
p1 = m - 1
p2 = n - 1
p = m + n - 1 # 初始化3个指针,p作为辅助指针
while p1 > -1 and p2 > -1: # 逆序进行
if nums1[p1] < nums2[p2]: # 由于是从后往前确定位置,所以这里应该是先存放大的一个元素
nums1[p] = nums2[p2]
p2 -= 1
else:
nums1[p] = nums1[p1]
p1 -= 1
p -= 1
# 由于是在nums1中做原地变换,因此在nums1中的元素仍然在nums1中;
# 而排序完后nums2中可能存在剩余元素,则按照nums2中原顺序依次添加至最前面
# 法一:
while p2 > -1:
nums1[p] = nums2[p2]
p -= 1
p2 -= 1
# 法二:
# nums1[: p2+1] = nums2[: p2+1]