- 标签:数组、二分查找、分治算法
- 难度:困难
给定两个正序(从小到大排序)数组 nums1、nums2。要求找出并返回这两个正序数组的中位数。
单个有序数组的中位数是中间元素位置的元素。如果中间元素位置有两个元素,则为两个元素的平均数。如果是两个有序数组,则可以使用归并排序的方式将两个数组拼接为一个大的有序数组。大的有序数组中间位置的元素,即为中位数。
当然不合并的话,我们只需找到中位数的位置即可。我们用 n1、n2 来表示数组 nums1、nums2 的长度,则合并后的大的有序数组长度为 (n1 + n2)。
同时可以发现:中位数把数组分割成了左右两部分,并且左右两部分元素个数相等。
- 如果
$(n1 + n2)$ 是奇数时,中位数是大的有序数组中第$\lfloor \frac{(n1 + n2)}{2} \rfloor + 1$ 的元素,单侧元素个数为$\lfloor \frac{(n1 + n2)}{2} \rfloor + 1$ 个(包含中位数)。 - 如果
$(n1 + n2)$ 是偶数时,中位数是第$\lfloor \frac{(n1 + n2)}{2} \rfloor$ 的元素和第$\lfloor \frac{(n1 + n2)}{2} \rfloor + 1$ 的元素的平均值,单侧元素个数为$\lfloor \frac{(n1 + n2)}{2} \rfloor$ 个。
因为是向下取整,上面两种情况综合可以写为:单侧元素个数为:$\lfloor \frac{(n1 + n2 + 1)}{2} \rfloor$ 个。
我们用
如果我们从 nums1 数组中取出 m1 个元素,那么从 nums2 就需要取出 m2 = k - m1 个元素。
问题就可以进一步转换为:从 nums1 数组中取出 m1 个元素,那么从 nums2 就需要取出 k - m1 个元素,使得 nums1 第 m1 个元素或 nums2 第 k - m1 个元素为中位线位置。
可以通过「二分查找」来找到合适的 m1 位置。
让 left 指向
每次取中间位置
如果
简单可以描述为:
-
如果
$nums1[mid] < nums2[k- mid - 1]$ ,则$nums1$ 的前$mid$ 个元素都不可能是第$k$ 个元素,可以全部排除。 -
如果
$nums1[mid] > nums2[k- mid - 1]$ ,则$nums2$ 的前$mid$ 个元素都不可能是第$k$ 个元素,可以全部排除。 -
如果
$nums1[mid] == nums2[k- mid - 1]$ ,可以按第一种情况处理。
找到 m1 的位置之后,还要根据两个数组长度和
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
n1 = len(nums1)
n2 = len(nums2)
if n1 > n2:
return self.findMedianSortedArrays(nums2, nums1)
k = (n1 + n2 + 1) // 2
left = 0
right = n1
while left < right:
mid = left + (right - left) // 2
if nums1[mid] < nums2[k-1-mid]:
left = mid + 1
else:
right = mid
m1 = left
m2 = k - m1
c1 = max(float('-inf') if m1 <= 0 else nums1[m1-1], float('-inf') if m2 <= 0 else nums2[m2 - 1])
if (n1 + n2) % 2 == 1:
return c1
c2 = min(float('inf') if m1 >= n1 else nums1[m1], float('inf') if m2 >= n2 else nums2[m2])
return (c1 + c2) / 2