剑指Offer 51. 数组中的逆序对(Hard)/ 88. 合并两个有序数组(Easy)/ 计算数组的小和 / 315. 计算右侧小于当前元素的个数(Hard)

本文介绍了一种高效计算数组中逆序对数量的方法,利用归并排序改进暴力解法,显著提升了算法效率。适用于解决诸如计算右侧小于当前元素的个数等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

在这里插入图片描述
【题目链接】

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution:
    ### 1108 暴力遍历(超时)
    def reversePairs(self, nums: List[int]) -> int:
        count = 0
        nums_len = len(nums)
        for cur_idx in range(nums_len):
            cur_num = nums[cur_idx]
            for next_idx in range(cur_idx+1, nums_len):
                if cur_num > nums[next_idx]:
                    count += 1
        return count

    ### 1108 使用归并排序求逆序数(1340 ms,18.2 MB)
    def mergeSort(self, nums, tmp, l, r):
        if l >= r: # 若当前子数组只有一个元素,则默认其为有序数组,直接返回(0)
            return 0
        
        # 归:每次将原数组(尽可能)一分为二,然后对两个子数组分别进行归并排序(分而治之)
        mid = (l + r) // 2
        inv_cnt = self.mergeSort(nums, tmp, l, mid) + self.mergeSort(nums, tmp, mid+1, r) # 左右两边同时进行(子)归并排序、计算各自(子)归并排序的逆序数,并把它们加起来

        # 并:将已经排序好的两个子数组,继续排序成一个顺序的父数组
        i, j, pos = l, mid+1, l # 初始化3个指针:i为左子数组L的指针(指向数组开始位置),j为右子数组R的指针(指向数组R开始位置),pos为辅助数组指针
        while i <= mid and j <= r: # 当i还未遍历完数组L且j还未遍历数组R时,重复循环
            if nums[i] <= nums[j]: # 比较两个子数组中所指向的值的大小,将较小的值存放进辅助数组tmp
                tmp[pos] = nums[i]
                i += 1
                inv_cnt += (j - (mid+1)) # 若较小值在数组L中,则R中位于j之前的值均与L中i指向的值构成逆序数,则统计逆序数
            else:
                tmp[pos] = nums[j]       # 若较小值在数组R中,则不存在逆序数
                j += 1
            pos += 1
        # 检查子数组L中是否存在剩余的元素,若存在则将剩余元素追加进辅助数组tmp,并继续统计逆序数
        for k in range(i, mid+1):
            tmp[pos] = nums[k]
            pos += 1
            inv_cnt += (j - (mid+1))
        # 检查子数组L中是否存在剩余的元素,若存在则将剩余元素追加进辅助数组tmp,此时不存在逆序数
        for k in range(j, r+1):
            tmp[pos] = nums[k]
            pos += 1
        # 将已经排序好的子数组的元素返回至原数组中原来的位置(原地排序,且归并排序是稳定排序)
        nums[l: r+1] = tmp[l : r+1]
        
        return inv_cnt

    def reversePairs(self, nums: List[int]) -> int:
        tmp = [0] * len(nums)
        return self.mergeSort(nums, tmp, 0, len(nums)-1)
### 二刷
class Solution:
    def reversePairs(self, nums: List[int]) -> int:
        def sort(tmp, l, r):
            if l >= r: return 0

            mid = (l+r) // 2
            inv_cnt = sort(tmp, l, mid) + sort(tmp, mid+1, r)

            i, j, p = l, mid+1, l # j = mid+1

            while i <= mid and j <= r:
                if nums[i] <= nums[j]: # 取等!
                    tmp[p] = nums[i]
                    i += 1
                    inv_cnt += j - (mid+1) # j - (mid+1)
                else:
                    tmp[p] = nums[j]
                    j += 1
                p += 1

            for k in range(i, mid+1):
                tmp[p] = nums[k]
                p += 1
                inv_cnt += j - (mid+1)
            for k in range(j, r+1):
                tmp[p] = nums[k]
                p += 1

            nums[l:r+1] = tmp[l:r+1]
        
            return inv_cnt
    
        l, r = 0, len(nums)-1
        tmp = [0] * len(nums) # 与nums长度相同

        return sort(tmp, l, r)
  • 另附1:归并排序代码
class Solution:
    ### 1108 归并排序
    def mergeSort(self, nums, tmp, l, r):
        if l >= r: # 若当前子数组只有一个元素,则默认其为有序数组,直接返回
            return
        
        # 归:每次将原数组(尽可能)一分为二,然后对两个子数组分别进行归并排序(分而治之)
        mid = (l + r) // 2
        self.mergeSort(nums, tmp, l, mid)   # 左数组L进行(子)归并排序
        self.mergeSort(nums, tmp, mid+1, r) # 右数组R进行(子)归并排序

        # 并:将已经排序好的两个子数组,继续排序成一个顺序的父数组
        i, j, pos = l, mid+1, l # 初始化3个指针:i为左子数组L的指针(指向数组开始位置),j为右子数组R的指针(指向数组R开始位置),pos为辅助数组指针
        while i <= mid and j <= r: # 当i还未遍历完数组L且j还未遍历数组R时,重复循环
            if nums[i] <= nums[j]: # 比较两个子数组中所指向的值的大小,将较小的值存放进辅助数组tmp
                tmp[pos] = nums[i] # 若较小值在数组L中
                i += 1
            else:
                tmp[pos] = nums[j]       # 若较小值在数组R中
                j += 1
            pos += 1
        # 检查子数组L中是否存在剩余的元素,若存在则将剩余元素追加进辅助数组tmp
        for k in range(i, mid+1):
            tmp[pos] = nums[k]
            pos += 1
        # 检查子数组L中是否存在剩余的元素,若存在则将剩余元素追加进辅助数组tmp
        for k in range(j, r+1):
            tmp[pos] = nums[k]
            pos += 1
        # 将已经排序好的子数组的元素返回至原数组中原来的位置(原地排序,且归并排序是稳定排序)
        nums[l: r+1] = tmp[l : r+1]
        
        return

    def reversePairs(self, nums: List[int]) -> int:
        tmp = [0] * len(nums)
        return self.mergeSort(nums, tmp, 0, len(nums)-1)

88. 合并两个有序数组(Easy)

  1. 双指针 + 从前往后 / 从小到大:时间复杂度O(n+m),空间复杂度O(m)
  2. 双指针 + 从后往前 / 从大到小:时间复杂度O(n+m),空间复杂度O(1)
class Solution:
    ### 1108 双指针 + 从前往后 / 从小到大(	40 ms,13.6 MB)
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        nums1_cpy = nums1[:m] # 引入辅助数组来保存nums1中的元素
        nums1[:] = []         # 使用[:]来保证变量nums1的地址不变,也即原地变换in-place

        ptr1 = ptr2 = 0 # 初始化双指针为0

        while ptr1 < m and ptr2 < n:          # 顺序进行
            if nums1_cpy[ptr1] > nums2[ptr2]: # 由于是从后往前确定位置,所以这里应该是先存放小的一个元素
                nums1.append(nums2[ptr2])
                ptr2 += 1
            else:
                nums1.append(nums1_cpy[ptr1])
                ptr1 += 1

        # 若nums1或nums2中存在剩余元素,则直接追加
        if ptr1 < m:
            nums1[ptr1+ptr2: ] = nums1_cpy[ptr1: ]
        if ptr2 < n:
            nums1[ptr1+ptr2: ] = nums2[ptr2: ]

    ### 1108 双指针 + 从后往前 / 从大到小(52 ms,13.5 MB)
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        p1 = m - 1
        p2 = n - 1
        p = m + n - 1 # 初始化3个指针,p作为辅助指针
        
        while p1 > -1 and p2 > -1:    # 逆序进行
            if nums1[p1] < nums2[p2]: # 由于是从后往前确定位置,所以这里应该是先存放大的一个元素
                nums1[p] = nums2[p2]
                p2 -= 1
            else:
                nums1[p] = nums1[p1]
                p1 -= 1
            p -= 1
        
        # 由于是在nums1中做原地变换,因此在nums1中的元素仍然在nums1中;
        # 而排序完后nums2中可能存在剩余元素,则按照nums2中原顺序依次添加至最前面
        # 法一:
        while p2 > -1:
            nums1[p] = nums2[p2]
            p -= 1
            p2 -= 1
       	# 法二:
        # nums1[: p2+1] = nums2[: p2+1] 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值