剑指 Offer 51. 数组中的逆序对

Posted 炫云云

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指 Offer 51. 数组中的逆序对相关的知识,希望对你有一定的参考价值。

剑指 Offer 51. 数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

示例 1:

输入: [7,5,6,4]
输出: 5

我们接下来介绍更广泛使用的,效率更高的解法 分治。 我们进行一次归并排序,并在归并过程中计算逆序数,换句话说 逆序对是归并排序的副产物

05 归并排序

归并排序实际上会把数组分成两个有序部分,我们不妨称其为左和右,归并排序的过程中会将左右两部分合并成一个有序的部分,对于每一个左右部分,我们分别计算其逆序数,然后全部加起来就是我们要求的逆序数。 那么分别如何求解左右部分的逆序数呢?

首先我们知道归并排序的核心在于合并,而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 。 其中 p1p2 指针如粗体部分(左数组的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

参考

Krahets - 力扣(LeetCode) (leetcode-cn.com)

以上是关于剑指 Offer 51. 数组中的逆序对的主要内容,如果未能解决你的问题,请参考以下文章

剑指offer面试题51:数组中的逆序对

剑指 Offer 51. 数组中的逆序对

剑指offer(51)

[剑指offer]51-数组中的逆序对(归并排序)

剑指 Offer 51. 数组中的逆序对

剑指 Offer 51. 数组中的逆序对