- 数组中的逆序对
class Solution:
def reversePairs(self, nums: List[int]) -> int:
self.cnt = 0
def merge(nums, start, mid, end, temp):
i, j = start, mid + 1
while i <= mid and j <= end:
if nums[i] <= nums[j]:
temp.append(nums[i])
i += 1
else:
self.cnt += mid - i + 1
temp.append(nums[j])
j += 1
while i <= mid:
temp.append(nums[i])
i += 1
while j <= end:
temp.append(nums[j])
j += 1
for i in range(len(temp)):
nums[start + i] = temp[i]
temp.clear()
def mergeSort(nums, start, end, temp):
if start >= end: return
mid = (start + end) // 2
mergeSort(nums, start, mid, temp)
mergeSort(nums, mid + 1, end, temp)
merge(nums, start, mid, end, temp)
mergeSort(nums, 0, len(nums) - 1, [])
return self.cnt
归并排序的关键是并,并的过程中对两个有序数组进行合并,合并的过程中能够计算逆序对的个数。
- 计算右侧小于当前元素的个数
class Solution:
def countSmaller(self, nums):
size = len(nums)
if size == 0:
return []
if size == 1:
return [0]
temp = [None for _ in range(size)]
indexes = [i for i in range(size)]
res = [0 for _ in range(size)]
self.__helper(nums, 0, size - 1, temp, indexes, res)
return res
def __helper(self, nums, left, right, temp, indexes, res):
if left == right:
return
mid = left + (right - left) // 2
# 计算一下左边
self.__helper(nums, left, mid, temp, indexes, res)
# 计算一下右边
self.__helper(nums, mid + 1, right, temp, indexes, res)
if nums[indexes[mid]] <= nums[indexes[mid + 1]]:
return
self.__sort_and_count_smaller(nums, left, mid, right, temp, indexes, res)
def __sort_and_count_smaller(self, nums, left, mid, right, temp, indexes, res):
# [left,mid] 前有序数组
# [mid+1,right] 后有序数组
# 先拷贝,再合并
for i in range(left, right + 1):
temp[i] = indexes[i]
l = left
r = mid + 1
for i in range(left, right + 1):
if l > mid:
# l 用完,就拼命使用 r
# [1,2,3,4] [5,6,7,8]
indexes[i] = temp[r]
r += 1
elif r > right:
# r 用完,就拼命使用 l
# [6,7,8,9] [1,2,3,4]
indexes[i] = temp[l]
l += 1
# 注意:此时前面剩下的数,比后面所有的数都大
res[indexes[i]] += (right - mid)
elif nums[temp[l]] <= nums[temp[r]]:
# [3,5,7,9] [4,6,8,10]
indexes[i] = temp[l]
l += 1
# 注意:
res[indexes[i]] += (r - mid - 1)
else:
assert nums[temp[l]] > nums[temp[r]]
# 上面两种情况只在其中一种统计就可以了
# [3,5,7,9] [4,6,8,10]
indexes[i] = temp[r]
r += 1
同样是算个数,但是精确到了每个元素的个体,这样需要索引数组来记录每次排序完的元素顺序。
class Solution:
def countSmaller(self, nums):
if not nums:
return []
counts = [0]
sorts = [nums[-1]]
for i in range(len(nums)-2,-1,-1):
ele = nums[i]
l = 0
r = len(sorts)
while l < r:
mid = (l + r)//2
if sorts[mid] >= ele:
r = mid
else:
l = mid + 1
index = l
sorts.insert(index,ele)
counts.append(index)
return counts[::-1]
更巧妙的方法:开辟一个数组,从后往前读取元素,并使用二分查找,找出数组的下限,即小于当前元素的个数。
复习:
二分查找无重复元素版:
mid = (left + right) // 2
其实要逻辑弄清楚:
这里的right = len(nums) - 1
while left <= right:
mid < element : left = mid + 1
mid > element : right = mid - 1
mid == element : return mid
return -1
二分查找 上限与下限:
下限:
这里的right = len(nums)
while left < right:
mid >= element: right = mid
mid < element: left = mid + 1
return left
上限:
while left < right:
mid <= element: left = mid + 1
mid > element : right = mid
return left