leetcode(5)_4_hard_寻找两个正序数组的中位数_python

寻找两个正序数组的中位数

题目描述:
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
进阶: 你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
提示:

  • nums1.length == m,nums2.length == n
  • 0 <= m <= 1000,0 <= n <= 1000,1 <= m + n <= 2000
  • -106 <= nums1[i], nums2[i] <= 106

解法

  1. 这道题很多地方都有看到,但一直没有冷静下来解决掉,主要还是中位数需要考虑奇偶的问题,感觉处理起来很麻烦;另外,把复杂度降到O(log(m + n)),肯定是要二分,所以始终给我一种感觉,就是需要分别找nums1nums2的中点,然后进行一些判断,抛去一半长度的搜索区间。这种思路导致我不能成功做出来;做出不来就会看题解,一下就会了,又担心直接写记不住,没效果,准备隔几天再写;然后几天后又回到起始的思路…
  2. 言归正传,如果把寻找中位数转为寻找第 k 小,那么这道题其实就不难了。我们仍然使用前面的记法,记nums1的长度为mnums2的长度为n,如果m + n为奇数,那么找到第(m + n + 1)//2小的元素即可;如果为偶数,那么找到第(m + n)//2(m + n)//2 + 1小的元素,再取平均即可。
  3. 可以看出,我们只需写一个新方法——取两个数组的第 k 小。因为两个数组都是有序的,所以有以下思路:
    第一步我们初始化两个数组的搜索左边界left1, left2都是0,然后可以比较nums1[left1 + k//2 - 1]nums2[left2 + k//2 - 1]的大小,如果前者大,那就说明目标元素肯定不在nums2[left2: left2 + k//2]中;反之就不在nums1[left1: left1 + k//2]中。
    第二步我们更新左边界,并且更新 k 值。不妨假设第一步的结论是,目标元素不在nums2[left2: left2 + k//2]中,那么就可以把nums2的搜索左边界设置为left2 + k//2,并且把 k 更新为k - k//2
    第三步就是在新的搜索空间中,寻找第 k 小的元素。
    需要注意的是,left1 + k//2 - 1或者left2 + k//2 - 1可能是越界的,需要和数组长度取min。整个过程是不断循环的,所以可以写成while循环或者递归的形式,终止条件是:如果k == 1,那么就取两个数组左边界中的最小值;如果某一数组的左边界已经越界,那就直接取另一数组的第 k 小即可。
代码
class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        total_len = len(nums1) + len(nums2)
        if total_len % 2:
            return self.func(nums1, nums2, 0, 0, (total_len + 1) // 2)
        return (self.func(nums1, nums2, 0, 0, total_len//2) + self.func(nums1, nums2, 0, 0,total_len//2 + 1)) / 2
    
    def func(self, nums1, nums2, left1, left2, k):
        m, n = len(nums1), len(nums2)
        if m <= left1:  # 越界
            return nums2[left2 + k - 1]
        if n <= left2:  # 越界
            return nums1[left1 + k - 1]
        if k == 1:  # 相当于取两数组的最小值
            return min(nums1[left1], nums2[left2])
        pos1, pos2 = min(m - 1, left1 + k//2 - 1), min(n - 1, left2 + k//2 - 1)  # 设置两个数组的比较元素
        if nums1[pos1] > nums2[pos2]:  # 更新 left2 和 k
            k -= pos2 - left2 + 1
            left2 = pos2 + 1
        else:  # 更新 left1 和 k
            k -= pos1 - left1 + 1
            left1 = pos1 + 1
        return self.func(nums1, nums2, left1, left2, k)
      
测试结果

执行用时:52 ms, 在所有 Python3 提交中击败了72.87% 的用户
内存消耗:15 MB, 在所有 Python3 提交中击败了60.63% 的用户

说明

算法题来源:力扣(LeetCode)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值