剑指 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
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值