对于单个数组的中位数很好求,如果个数是奇数则中位数为array[n/2],个数是偶数则中位数为(array[n/2]+array[n/2-1])/2。对于两个数组的中位数实际就是合并后数组的中位数,但是题目要求复杂度为O(log(m+n)),因此先合并再求中位数的方法放弃。可以从两数组的元素大小顺序入手,奇数数组是左中位数和右中位数和的一半,偶数数组的中位数就是右中位数(第n/2+1个数),因此只要找到从小到大第n/2+1个数就知道到了结果。
假设数组a的长度和数组b的长度之和为k,则有以下关系:
将两数组都定位在k/2处,如果数组a[k/2-1]和数组b[k/2-1]相等,则两个合并的数组的第k个数就是在此位置。
如果a[k/2-1]比b[k/2-1]小,则第k个数在数组a的k/2处的右边和数组b的k/2处的左边范围,即图中红色的范围。
如果a[k/2-1]比b[k/2-1]大,则第k个数在数组a的k/2处的左边和数组b的k/2处的右边范围,即图中绿色的范围。
这种查找方法用到了二分查找的思想。
注意的问题是分割线的选择,如果数组a(默认它是较短的数组)的长度比k/2要小,则选择分割线就在数组a的最右边,即lengthOfArraya。
下面是代码实现,用时60ms
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int len1=nums1.size(),len2=nums2.size();
int len=len1+len2;
if(len%2==0)
{
return 0.5*(findKthVal(nums1,0,len1,nums2,0,len2,len/2)+
findKthVal(nums1,0,len1,nums2,0,len2,len/2+1));
}
else
{
return findKthVal(nums1,0,len1,nums2,0,len2,len/2+1);
}
}
double findKthVal(vector<int>& nums1, int start1, int Length1, vector<int>& nums2, int start2, int Length2,int k)
{
if(Length1>Length2) //确保nums1始终是较短的向量
return findKthVal(nums2,start2,Length2,nums1,start1,Length1,k);
if(Length1==0&&Length2>0)
return nums2[start2+k-1]; //如果nums1为空,则说明中位数就是nums2的第k个数
if(k==1) //这种情景对应nums1和nums2中参加比较的数的个数都为1,则左中位数就是两者中较小的一个
return nums1[start1]<nums2[start2]?nums1[start1]:nums2[start2];
int L1=Length1<k/2 ? Length1:k/2; //从中间位置开始二分查找
int L2=k-L1;
if(nums1[start1+L1-1]==nums2[start2+L2-1])
return nums1[start1+L1-1];
else if(nums1[start1+L1-1]<nums2[start2+L2-1])
return findKthVal(nums1,start1+L1,Length1-L1,nums2,start2,L2,k-L1);
else
return findKthVal(nums1,start1,L1,nums2,start2+L2,Length2-L2,k-L2);
}
};