给定两个大小为 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
好的,看了这个题目首先要注意的一点是时间复杂度问题,我们必须要在log时间内算出,要不一合并就完事了嘛。
官方题解说了一大堆,说白了是用二分搜索寻找分割点。
首先想一个事情,如果一个序列a0,a1....an是有序的,那么我们如何寻找这个中位数呢?
我们想到的是分奇数个还是偶数个(题目已经给了提示了)假如是2k个数字 比如 0,1,2,3,4,5六个数
我们应该返回(2+3)/2那就是2.5,就是说(A[k-1]+A[k])/2
如果是奇数个比如0,1,2,3,4, 2k+1个数字应该返回A[k-1]=2。
那么当有2个序列呢?我们可以把序列“假象一样”合并起来。
比如序列1:1,3,5,7
序列2:2,4,6,8,10,12
一共10个数字,那么我们要的是第5和第6个数字。
线性的搜索一下那就是:遍历序列1,当我分成1 3,5,7时,为了分成两半我们就要把序列2分为 2,4,6,8 10,12
因为只有这样才能保证左右合并后各一半嘛。
但是我们这样并不满足条件,因为左半边最大值:8是比右半边最小值:3还要大的。那么这样的话我们就要继续搜索。
我发现分成1,3 5,7 和2,4,6 8,10,12 时恰好满足条件。所以用左半边最大值6与右半边最小值5取中间值5.5就是答案。
综合来看,实际上我们要寻找的是这样的一个划分点:
1、保证A序列左半边和B序列左半边长度为长度和的一半(实际上也可能比一半少1)。
2、满足1的前提下应当左半部分最大值小于等于右半部分最小值。
那么线性搜索再分割就十分容易理解了。接下来想一想如何用二分搜索法来寻找这个点。
总的概括这个过程就是,当分割点选的“太小”,就把右边界左移,“太大”把左边界右移(是不是很像二分查找法)
class Solution:
def findMedianSortedArrays(self, nums1, nums2) -> float:
M,N=len(nums1),len(nums2)
if M==0:
return nums2[N//2] if N%2!=0 else (nums2[N//2]+nums2[N//2-1])/2
elif N==0:
return nums1[M//2] if M%2!=0 else (nums1[M//2]+nums1[M//2-1])/2
else:
if M>N:
nums1,nums2=nums2,nums1
N,M=M,N
half=(M+N+1)//2
lo,hi=0,M
while lo<=hi:
i=(lo+hi)//2
j=half-i
print("i=",i," j=",j)
if i<hi and nums2[j-1]>nums1[i]:
lo=i+1
elif lo<i and nums1[i-1]>nums2[j]:
hi=i-1
else:
if i==0:
left_max=nums2[j-1]
elif j==0:
left_max=nums1[i-1]
else:
left_max=max(nums1[i-1],nums2[j-1])
if i==M:
right_min=nums2[j]
elif j==N:
right_min=nums1[i]
else:
right_min=min(nums2[j],nums1[i])
return (left_max+right_min)/2 if (M+N)%2==0 else left_max
顺带说一句,如果不按照题解先把nums1变成短的那个,那变量j就会莫名其妙越界。试了几组数据都这样。