题目
数组中的逆序对
一、暴力遍历
暴力遍历数组,每遍历到一个数字,遍历其后面的所有数字,判断是否组成逆序对。
时间复杂度O(n^2)
def inverse_pari_1(arr):
if not arr or len(arr) == 1:
return 0
res = 0
for i in range(len(arr)):
for j in range(i+1, len(arr)):
if arr[i] > arr[j]:
res += 1
return res
二、分治归并
在【分】的时候,依旧每次分裂左右两部分
但【合】的时候,需要在调整顺序的同时,计算逆序对。
对于【7】,【5】这种情况,很明显能看到7的逆序对的贡献度为1,5的逆序对贡献度为0,和为1.
在【3,5,7】,【1,2,4】中:
假设有两个指针l_p、r_p分别指向left和right子数组的头部。
r_p指向的元素更小,放入res且r_p向右挪动一个指针指向2。
此时l_p指向3,r_p指向2,r_p指向的元素更小,放入res且r_p向右挪动一个指针指向4。
此时l_p指向3,r_p指向4,l_p指向的元素更小,放入res且累计逆序对的贡献度。r_p此时指向2,因此有2个元素(1,2)比左边的3更小,因此3在右边子数组的逆序对有2个。l_p向右挪动一个指针指向5。
此时l_p指向5,r_p指向4,r_p指向的元素更小,放入res且r_p向右挪动一个指针(此时指针为3,越界)结束遍历。
这个时候需要遍历指针未越界的list,在这里是左子数组。当左子数组剩下的数值都大于右子数组是,当前的5与右子数组组成的逆序对为右子数组的长度(【5,1】,【5,2】,【5,4】)
def inverse_pari_2(arr):
if not arr:
return 0
if len(arr) == 1:
return arr,0
left = inverse_pari_2(arr[: (len(arr) // 2)])
right = inverse_pari_2(arr[(len(arr) // 2):])
r = merge(left[0], right[0])
return r[0], r[1]+left[1]+right[1]
def merge(left, right):
h, j, res, pairs = 0, 0, [], 0
while h < len(left) and j < len(right):
if left[h] > right[j]:
res.append(right[j])
j += 1
else:
res.append(left[h])
pairs += j
h += 1
if h < len(left):
for num in left[h:]:
res.append(num)
pairs += len(right)
else:
for num in right[j:]:
res.append(num)
return res, pairs
大神的更简洁高效的实现方法~~
def mergeSort(nums, tmp, l, r):
if l >= r:
return 0
mid = (l + r) // 2
inv_count = mergeSort(nums, tmp, l, mid) + mergeSort(nums, tmp, mid + 1, r)
i, j, pos = l, mid + 1, l
while i <= mid and j <= r:
if nums[i] <= nums[j]:
tmp[pos] = nums[i]
i += 1
inv_count += (j - (mid + 1))
else:
tmp[pos] = nums[j]
j += 1
pos += 1
for k in range(i, mid + 1):
tmp[pos] = nums[k]
inv_count += (j - (mid + 1))
pos += 1
for k in range(j, r + 1):
tmp[pos] = nums[k]
pos += 1
nums[l:r+1] = tmp[l:r+1]
return inv_count
def reversePairs(nums):
n = len(nums)
tmp = [0] * n
return mergeSort(nums, tmp, 0, n - 1)