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

Posted 风去幽墨

tags:

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

题目链接:

https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/

题意:

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

题解:

解法1:暴力(TLE)
两层遍历,找每个数字之后比其小的所有数字

解法2:归并排序(1932 msms,28.2mb)
利用归并排序中的归并过程。
归并时记左区间指针为i,右区间指针为j。
当nums[i]>nums[j]时,应该添加nums[j]到新数组中去,这时因为左右两个子区间是各自有序的,所以nums[j]<nums[i]<nums[i+1]<…nums[mid]且j>mid>mid-1>…>i+1>i。所以这里可以找到一些逆序对(nums[j]与左区间部分)其数量共为mid-i+1。
因为归并排序在处理子区间有序的时候并不会改变左右两个区间的位置关系。所以所有的逆序对都会被计算且仅计算一次。

解法3:离散化+树状数组(1820ms,20mb),面试过程中极少被问到
思想与暴力类似,但处理方法有所不同。
主要思想:
从后向前遍历,遍历到每个值的时候,记cnt[nums[i]]为值为nums[i]的元素已经出现的次数。与当前元素构成逆序对的元素是已经遍历过的且数值比nums[i]小的元素的数量之和。数量之和又可以通过前缀和来记录。

  • Q:n范围为[0,50000],但nums[i]的范围很大(整型),没有那么度空间去存储每个数字出现的次数怎么办?
  • A:离散化,即给每个值排个名(该值在n个值中排老几),最终离散化nums[i]的范围就变成了[1,n],空间就省下来了。
  • Q:前缀和在随着遍历过程中不断变化该怎么计算?
  • A:利用树状数组、线段树等可以在线修改及区间查询的数据结构解决。这里使用树状数组即可。

代码:

#法一
class Solution:
    def reversePairs(self, nums: List[int]) -> int:
        if not nums: return 0
        ans = 0
        def merge(st,ed):
            nonlocal ans
            if st == ed :
                return 
            mid =(st+ed)//2
            merge(st,mid)
            merge(mid+1,ed)
            a,i,j=[],st,mid+1
            while i<=mid and j<=ed :
                if nums[i]<=nums[j]:
                    a.append(nums[i])
                    i+=1
                else:
                    a.append(nums[j])
                    ans += mid-i+1
                    # print(st,ed,ans)
                    j+=1
            while i<=mid:
                a.append(nums[i])
                i+=1
            while j<=ed:
                a.append(nums[j])
                j+=1
            for i in range(st,ed+1):
                nums[i]=a[i-st]
        merge(0,len(nums)-1)
        # print(nums)
        return ans
#法二
class BIT:
    def __init__(self, n):
        self.n = n
        self.tree = [0] * (n + 1)

    @staticmethod
    def lowbit(x):
        return x & (-x)
    
    def query(self, x):
        ret = 0
        while x > 0:
            ret += self.tree[x]
            x -= BIT.lowbit(x)
        return ret

    def update(self, x):
        while x <= self.n:
            self.tree[x] += 1
            x += BIT.lowbit(x)

class Solution:
    def reversePairs(self, nums: List[int]) -> int:
        n = len(nums)
        # 离散化
        tmp = sorted(nums)
        for i in range(n):
            nums[i] = bisect.bisect_left(tmp, nums[i]) + 1
        # 树状数组统计逆序对
        bit = BIT(n)
        ans = 0
        for i in range(n - 1, -1, -1):
            ans += bit.query(nums[i] - 1)
            bit.update(nums[i])
        return ans

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

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

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

剑指offer(51)

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

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

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