Given two sorted arrays nums1
and nums2
of size m
and n
respectively, return the median of the two sorted arrays.
The overall run time complexity should be O(log (m+n))
.
Example 1:
Input: nums1 = [1,3], nums2 = [2] Output: 2.00000 Explanation: merged array = [1,2,3] and median is 2.
Example 2:
Input: nums1 = [1,2], nums2 = [3,4] Output: 2.50000 Explanation: merged array = [1,2,3,4] and median is (2 + 3) / 2 = 2.5.
Constraints:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106
题目关键字有序数组以及时间复杂度O(log(m+n)),提醒我们应该用二分查找法。这题的难点是该如何用二分查找法。题目要求的是从两个有序数组中找出它们的中位数。所谓中位数就是一个有序数组中最中间的那个(总数为奇数)或那两个数(总数为偶数)。这题最简单的方法是把两个有序数组合并成一个有序数组然后就立即知道中位数了,但时间复杂度满足不了题目要求。因此还是得用二分查找法。
对于一个有序数组,二分查找的核心思想是把数组划分成两个区间,判断目标数一定落在某个区间或者一定不落在某一个区间,然后把一定不在的那个区间排除掉,使得查找区间长度减半。而本题给的是两个有序数组,该如何用二分查找呢?基本思想不变,从两个数组中各划出一个相等长度的区间,如果能知道目标数一定不在哪个区间,那就可以把该区间排除掉。
对于一个长度为m的数组nums1和一个长度为n的的数组nums2,它们的中位数就是两个数组所有数中第(m + n + 1) // 2小和第(m + n + 2) // 2小的数(如果m + n是奇数,两个中位数是同一个数,所以奇偶不用分开处理)。假设k = (m + n + 1) // 2 ,那我们的任务就是找第k小的数,可以分别从两个数组中划出一个区间,每个区间各包含前(k // 2) 个数,通过比较两个区间最大的两个数就可以排除掉其中一个区间。
1)如果nums1[k//2 - 1] < nums2[k//2 - 1],第一个数组nums1的区间可以排除掉。可以用反证法,假设nums1[k//2 - 1]就是第k小的数,nums2区间至少有一个数比nums1[k//2 - 1]大,这样两个区间小于等于nums1[k//2 - 1]的个数少于k,与nums1[k//2 - 1]就是第k小的数相矛盾。所以第k小的数不可能落在第一个数组nums1的区间。
2)如果nums1[k//2 - 1] > nums2[k//2 - 1],第二个数组nums2的区间可以排除掉,原因同上。
基于以上分析就可以实现二分查找算法,每次循环就可以使得查找区间长度减半,直到最后区间长度为1时就可以得到答案。算法实现时还需要处理几个特殊情况防止数组溢出。
1)当第一个数组可划分的区间为空时,可以直接返回第二个数组第k小的数。
2)当第二个数组可划分的区间为空时,可以直接返回第一个数组第k小的数。
3)当第一个数组可划分的区间不够k//2个数时,可以直接排除第二个数组的k//2区间。
4)当第二个数组可划分的区间不够k//2个数时,可以直接排除第一个数组的k//2区间。
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
m, n = len(nums1), len(nums2)
#if (m + n) is even, k1, k2 are medians, if odd, k1 and k2 are the same
k1,k2 = (m + n + 1) // 2, (m + n + 2) // 2
def findKth(k):
i, j = 0, 0
while i < m or j < n:
if i >= m:
return nums2[j + k - 1]
if j >= n:
return nums1[i + k - 1]
if k == 1:
return min(nums1[i], nums2[j])
#fetch k/2 from nums1 and nums2
i1, j1 = i + k // 2 - 1, j + k // 2 - 1
if i1 >= m: #nums1 less than k/2, first k/2 of nums2 can be removed
j = j1 + 1
elif j1 >= n:#nums2 less than k/2, first k/2 of nums1 can be removed
i = i1 + 1
elif nums1[i1] < nums2[j1]:
i = i1 + 1
else:
j = j1 + 1
k -= k // 2
return (findKth(k1) + findKth(k2)) / 2.0