LeetCode 寻找两个有序数组的中位数(二分)
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解: 两个有序数组,建议在O(log(m + n))复杂度下计算,因此容易想到是二分了,但是又是两个数组的中位数,不能合并后再二分,只能充分利用两数组的有序信息来二分结果.
首先回忆一下中位数的意义,在有序数组中位置居中的那个数,目前有两个数组,我们定数组长度为len1和len2,合并后的总长度为len1+len2,其中中位数肯定存在于len1+len2的中间位置,也就是(len1+len2)/2 ,考虑到总数的奇偶关系,我们向上取整,即(len1+len2+1)/2.
接着,我们知道中位数的位置必定在(len1+len2+1)/2,但因为这是混合数组,我们无法确定这个数是A数组还是B数组.
但是我们知道,在这(len1+len2+1)/2个数字中,肯定存在一些A数组和B数组的值,至于是谁,我们用一下关系进行二分搜索.
在这(len1+len2+1)/2个数字中,我们定义A数组可能出现了前i个数,而B数组可能出现了j=(len1+len2+1)/2-i个数,这个很好理解,即A数组中的前i个数与B数组中的前j个数组成了合并后数组的前半部分.
为什么要说’前半部分’,而不说’一半’,因为有序的大小关系也是有意义的,可以知道,前半部分的最大值,一定小于或等于后半部分的最小值.
通过这个关系,我们首先二分尝试A数组中到底是前几个数存在于混合后的前半部分.比如我们尝试了A[i]这个数,并通过之前的关系得到了B数组中可能取到的数B[j],这个关系即,A数组取前i个数的情况下B数组取前(len1+len2+1)/2-i个数.
通过比较我们发现,当A[i] 取到的值 大于 B[j-1]个数 ,且 B[j] 取到的值 大于 A[i-1]个数时,此时前半部分的最大值不是A[i]就是B[j].
为什么呢?因为我们其实在这个过程中,是尽量搜索前半部分的最大值,但是我们这个最大值搜索是建立在两个数组的基础上的,那么最大值可能来自A数组也可能来自B数组,于是在符合两数组前i大和前j大,且i+j=(len1+len2+1)/2这个条件的情况下,我们取到了两数组可能出现的最大值位置,以至于再大就会找到后半部分的值了.
最后处理边界问题,这组数总数是奇数时,只需取一个值即可,那么这个值就是A[i-1]或B[j-1].取最大值返回
而当是偶数个时,取前半部分的最大值以及后半部分的最小值加和除二即中位数,那么只需要对A[i]和B[j]取最小值,然后加上上一步中的最大值结果除2即可
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
l1=len(nums1)
l2=len(nums2)
if l1>l2:
l1,l2,nums1,nums2=l2,l1,nums2,nums1
l=0
r=l1
half=(l1+l2+1)//2
while l<=r:
mid=(l+r)//2
half_mid=half-mid
if mid<l1 and nums1[mid]<nums2[half_mid-1]:
l=mid+1
elif mid>0 and nums1[mid-1]>nums2[half_mid]:
r=mid-1
else:
if mid==0: ans= nums2[half_mid-1]
elif half_mid==0:ans= nums1[mid-1]
else:ans= max(nums1[mid-1],nums2[half_mid-1])
if (l1+l2)%2==1:
return ans
if mid==l1:res=nums2[half_mid]
elif half_mid==l2:res=nums1[mid]
else :res=min(nums2[half_mid],nums1[mid])
return (ans+res)/2