问题回顾
给定两个大小分别为 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
步骤一:问题分析与直觉
目标:找到两个有序数组合并后的中位数,要求时间复杂度为O(log(m + n))。
关键观察:
- 中位数定义:将合并后的数组分为左右两部分,左边所有元素 ≤ 右边所有元素,且左半部分长度与右半部分相等或大1。
- 分割点性质:若找到分割点使得左半部分的最大值 ≤ 右半部分的最小值,则中位数由这两个值决定。
步骤二:算法设计
1. 确保较短的数组进行二分查找
- 交换数组,使
nums1
是较短的数组。这可以优化时间复杂度至O(log(min(m, n)))。
2. 确定分割点
- 定义
i
为nums1
的分割点,j
为nums2
的分割点,满足:i + j = (m + n + 1) / 2
- 左半部分包含
nums1[0..i-1]
和nums2[0..j-1]
,右半部分包含nums1[i..m-1]
和nums2[j..n-1]
。
3. 边界条件处理
- 当
i=0
时:nums1
的左半部分为空 →nums1LeftMax = INT_MIN
。 - 当
i=m
时:nums1
的右半部分为空 →nums1RightMin = INT_MAX
。 - 当
j=0
时:nums2
的左半部分为空 →nums2LeftMax = INT_MIN
。 - 当
j=n
时:nums2
的右半部分为空 →nums2RightMin = INT_MAX
。
4. 调整分割点的条件
- 正确分割的条件:
nums1LeftMax ≤ nums2RightMin 且 nums2LeftMax ≤ nums1RightMin
- 条件不满足时的调整:
- 若
nums1LeftMax > nums2RightMin
:说明i
太大,需减小 →high = i - 1
。 - 若
nums2LeftMax > nums1RightMin
:说明i
太小,需增大 →low = i + 1
。
- 若
5. 计算中位数
- 总长度为奇数:中位数为左半部分的最大值
max(nums1LeftMax, nums2LeftMax)
。 - 总长度为偶数:中位数为
(max(nums1LeftMax, nums2LeftMax) + min(nums1RightMin, nums2RightMin)) / 2.0
。
步骤三:代码实现与注释
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
if(nums1.size()>nums2.size())//这里始终保证nums1是更小的数组 避免接下来数组越界。
{
return findMedianSortedArrays(nums2,nums1);
}
int m = nums1.size(),n = nums2.size(),totalleft = (m+n+1)/2; //左半部分总元素数 数组元素数量为奇数的时候就是中间数
int low = 0,high=m;
while(low<=high)
{
int i =