题目描述
给定两个大小为 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
思路分析
一个增序数组
要寻求一个数组的中位数,很容易让人联想到用二分法解决
对于偶数个元素的数组A,假定其元素个数为 n,下标从0开始,其中位数是 (A[(n-1)/2] + A[n/2])/2
对于偶数个元素的数组B,假定其元素个数为m,下标从0开始,其中位数是A[m/2]
隐含条件:任何一个存在中位数的数组,其中位数都会将数组分割成等长的两份,并且左边小于中位数,右边大于中位数
两个增序数组
接下来推广,从上面的结论很容易得出,对于两个增序数组A,B,如果数组A中存在一个下标 i ,数组B中存在一个下标 j ,使得 len(数组A+数组B的左边) == len(数组A+数组B的右边),并且存在一个数使得A和B的左边都比它小,A和B的右边都比它大,那这个数就是这两个增序数组的中位数。
left_part | Cut | right_part |
---|---|---|
A[0],A[1],…,A[i] | / | A[i+1],A[i+2],…,A[m-1] |
B[0],B[1],…,B[j] | / | B[j+1],B[j+2],…,B[n-1] |
用表达式来表示上面的两个条件就是
-
i+j == m-1-i + n-1-j
-
A[i] <= B[j+1] && B[j] <= A[i+1]
双数组长度的奇偶
一个数组的奇偶有两种情况,可以想象,如果考虑双数组的奇偶,将是会有 2*2 = 4种情况,这将会大大增加编程难度,那么有没有一种方法让我们不用考虑奇偶。
让数组恒为奇数
操作很简单,就是给每个元素的左右加一个字符,比如 ‘#’,长度变为2*n+1,ex:
before | len | after | len |
---|---|---|---|
{1,2,3} | 3 | {#,1,#,2,#,3,#} | 7 |
{4,5,6,7} | 4 | {#,4,#,5,#,6,#,7,#} | 9 |
这样加的好处不光解决了奇偶的问题,还让获取元素的方法化简了,对于任何一个元素,都可以通过 现在的位置/2 得到原来的位置,比如{#,1,#,2,#,3,#}中的2,它的位置是a[3],通过 3/2 == 1,可以得到原来的位置,{1,2,3}中的a[1] == 2,{#,4,#,5,#}中的5,位置是a[3],3/2 == 1,即{4,5,6,7}中的a[1] == 5
当然这个操作并不需要在代码里实现,这里只是帮助理解
等式变换
接下来看这个等式
- i+j == m-1-i + n-1-j
由于数组长度增加,第一个等式变为
- i+j == 2*m+1-1-i + 2*n+1-1-j
- i+j == 2*m-i + 2*n-j
- i+j == m+n
由此可得出 j == m+n-i,其中 i (0~2*m)
由于 j > 0,因此可以得出 m+n-i > 0,由 i 的取值范围,最终得出此关系成立的条件是 n>m,即第二个数组的大小一定要比第一个大。
分治策略
接下来看这个等式
- A[i] <= B[j+1] && B[j] <= A[i+1]
那么分治的策略就很明显了
如果 A[i] > B[j+1],说明A数组的左边太长了,应该向左二分
如果 B[j] > A[i+1],说明A数组的左边太短了,应该向右二分
临界点
如果 i == 0 或 j == 0 或 i == 2*m 或 j == 2*n,那么A[i+1],B[j+1]等等都不存在,此时,到达临界的数组整体可能都比另一数组中的某一值小(大),那么中值在另一数组中。
代码
#include <bits/stdc++.h>
using namespace std;
class Solution
{
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
{
int m = nums1.size();
int n = nums2.size();
if(m>n) //保证数组1长度<数组2长度
return findMedianSortedArrays(nums2,nums1);
int l1 = 0,l2 = 0,r1 = 0,r2 = 0,i = 0,j = 0,imin = 0,imax = 2*m; //下标为0~2*m
while(imin <= imax) //存在空数组的情况,因此要有=
{
i = (imax+imin)/2;
j = m+n-i;
l1 = (i == 0)?INT_MIN:nums1[(i-1)/2];
r1 = (i == 2*m)?INT_MAX:nums1[i/2];
l2 = (j == 0)?INT_MIN:nums2[(j-1)/2];
r2 = (j == 2*n)?INT_MAX:nums2[j/2];
if(l1>r2)
imax = i-1;
else if(l2>r1)
imin = i+1;
else
break;
}
return (max(l1,l2)+min(r1,r2))/2.0;
}
};
int main()
{
vector<int> a = {1,2,4,5};
vector<int> b = {3};
Solution Sve;
cout<<Sve.findMedianSortedArrays(a,b);
return 0;
}