剑指 Offer 51. 数组中的逆序对
Posted 炫云云
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指 Offer 51. 数组中的逆序对相关的知识,希望对你有一定的参考价值。
剑指 Offer 51. 数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
我们接下来介绍更广泛使用的,效率更高的解法 分治
。 我们进行一次归并排序,并在归并过程中计算逆序数,换句话说 逆序对是归并排序的副产物
。
归并排序实际上会把数组分成两个有序部分,我们不妨称其为左和右,归并排序的过程中会将左右两部分合并成一个有序的部分,对于每一个左右部分,我们分别计算其逆序数,然后全部加起来就是我们要求的逆序数。 那么分别如何求解左右部分的逆序数呢?
首先我们知道归并排序的核心在于合并,而88. 合并两个有序数组是一个简单题目。 我这里给贴一下大概算法:
class Solution:
def merge(nums1, nums2):
res = []
p1 = p2 = 0
while p1 < len(nums1) and p2 < len(nums2):
if nums1[p1] <= nums2[p2]:
res.append(nums1[p1])
p1 += 1
else:
res.append(nums2[p2])
p2 += 1
while p1 < len(nums1):
res.append(nums1[p1])
p1 += 1
while p2 < len(nums2):
res.append(nums2[p2])
p2 += 1
return res
而我们要做的就是在上面的合并过程中统计逆序数。
比如对于左:[1,2,3,4]右:[2,5]。是按照 [start, mid], [mid, end] 区间分割的,因此这里的 mid 为 3 。 其中 p1
,p2
指针如粗体部分(左数组的3和右数组的2)。 那么 逆序数就是 mid - p1 + 1
也就是 3 - 2 + 1 = 2
即(3,2)
和 (4,2)
。其原因在于如果 3 大于 2,那么 3 后面不用看了,肯定都大于 2。
之后会变成:[1,2,3,4] 右:[2,5] (左数组的3 和 右数组的5),继续按照上面的方法计算直到无法进行即可。
class Solution:
def reversePairs(self, nums: List[int]) -> int:
self.cnt = 0 # 逆序数
def merge(nums, start, mid, end):
p1, p2, temp = start, mid + 1, []
while p1 <= mid and p2 <= end:
if nums[p1] <= nums[p2]:
temp.append(nums[p1])
p1 +=1
else:
self.cnt += mid - p1 + 1
temp.append(nums[p2])
p2 +=1
while p1 <= mid:
temp.append(nums[p1])
p1 +=1
while p2 <= end:
temp.append(nums[p2])
p2 +=1
for i in range(len(temp)):
nums[start + i] = temp[i]
def mergeSort(nums, start, end):
if start >= end:
return
mid = start +(end-start)//2
mergeSort(nums, start,mid)
mergeSort(nums, mid+1,end)
merge(nums,start,mid,end)
mergeSort(nums, 0, len(nums) - 1)
return self.cnt
注意上述算法在mergeSort中我们每次都开辟一个新的temp,这样空间复杂度大概相当于NlogN,实际上我们完全每必要每次mergeSort都开辟一个新的,而是大家也都用一个。代码:
class Solution:
def reversePairs(self, nums: List[int]) -> int:
self.cnt = 0 # 逆序数
def merge(nums, start, mid, end,temp):
p1, p2 = start, mid + 1
while p1 <= mid and p2 <= end:
if nums[p1] <= nums[p2]:
temp.append(nums[p1])
p1 +=1
else:
self.cnt += mid - p1 + 1
temp.append(nums[p2])
p2 +=1
while p1 <= mid:
temp.append(nums[p1])
p1 +=1
while p2 <= end:
temp.append(nums[p2])
p2 +=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-start)//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
参考
以上是关于剑指 Offer 51. 数组中的逆序对的主要内容,如果未能解决你的问题,请参考以下文章