给定两个大小为 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
分析:一眼看去以为题目不难,但是发现要求的时间复杂度为O(log (m+n)),瞬间觉得难度为hard也是正常的了。既然是O(log(m+n))的复杂度,又是找数字的问题,想必大家第一反应都想到用二分法做。没错,我也是这样想的(大神可能还想到更高级的做法),只是对两个数组用二分法大大增加的难度。
我们知道,假设一个数组a的长度为len,查找中位数的话有两种情况,1:len为偶数,则中位数的值为(第len/2位置的值+第len/2+1位置的值)/2。2:len为奇数,则中位数的值为len/2+1位置上的值。
对两个数组进行二分法,先把长度小的那个数组放在前面,如果nums1的长度比nums2长,则交换两个数组的位置。现在假设我们求的时两个合并有序数组的第k位数字,则我们可以在第一个数组里先取到第k/2位数字,如果k/2个数字比第一个数组的长度还要长则把第一个数组的数字全都取,设取到的长度为p1,然后再第二个数组里取k-p1个数字,记取得的长度为p2.
此时可以分为两种情况
1:nums1[p1-1]<nums2[p2-1] ,证明nums1[p1-1]还没到第k个数字的位置,第k个数字在nums1[p1-1]后面且在nums2[p2-1]的前面。
2:nums1[p1-1]>nums2[p2-1],证明nums1取多了,第k个数字在nums1[p1-1]之前且再nums2[p2-1]之后。
除了以上正常用二分来解题,还有两个特殊的点需要注意,1:当nums1为空时,直接返回nums2[k-1]即可。2:当k==1时,直接返回nums1[0]和nums2[0]最小的那个。
有了这样的思路不难写出代码
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int len = nums1.size()+nums2.size();
if(len&1)
return helper(nums1,nums2,len/2+1);
else
return (helper(nums1,nums2,len/2)+helper(nums1,nums2,len/2+1))/2;
}
double helper(vector<int>& nums1,vector<int>& nums2,int k)
{
if(nums1.size()>nums2.size())
return helper(nums2,nums1,k);
if(nums1.size() == 0)
return nums2[k-1];
if(k == 1)
return min(nums1[0],nums2[0]);
int len = nums1.size();
int p1 = min(k/2,len);
int p2 = k-p1;
if(nums1[p1-1]<nums2[p2-1])
{
vector<int> v1(nums1.begin()+p1,nums1.end());
vector<int> v2(nums2.begin(),nums2.begin()+p2);
return helper(v1,v2,k-p1);
}
else
{
vector<int> v3(nums1.begin(),nums1.begin()+p1);
vector<int> v4(nums2.begin()+p2,nums2.end());
return helper(v3,v4,k-p2);
}
}
};