坐标力扣:
曾经做这题时,卡了我很长时间。现在故地重游,仍然重蹈覆辙。
所以我很有必要记录下这条题目。
要达到如题的时间复杂度,不难想到,所用的方法一定和二分法有关系。
甚至多想想也能想出大概的方法:分别设两个指针cut1,cut2,将第一个数组切割成 left1和right1,将第二个数组切割成left2和right2,用二分查找 找到left1和left2中的数都小于等于right1和right2中的数,则中位数在两个cut处产生。(我讲的远远没有leetcode里面各位大佬的题解详细,值得去一看)。
但是,这其中的很多边界,细节等,便是真正的难点所在。
m+n为奇数怎么办,为偶数怎么办,cut越界怎么办······
考虑到各个方面,正真要写出代码还是很困难的。
于是我想出了目前对我来说最容易理解的思路:
若m+n为偶数,不谈,就希望他是偶数,因为这样的话中位数就=(左边最大数+右边最小数)/2.0。
若m+n为奇数,这就很令人头秃。这里我提供一个比较巧妙的思路:
例如两个数组nums1=[1,3,5] nums2=[2,4]。
寻找这两个数组中的中位数 其实就相当于寻找num1'=[1,1,3,3,5,5]和nuns2'=[2,2,4,4]两个数组的中位数
此刻数组长度总数变成了偶数,而变化后的数组中任意一个数字的下标/2等于原数组中这个数字的下标。
那么这个数组*2的变化我们并不需要实际实现,只是看成“虚拟数组”,利用这个虚拟数组更方便地控制原数组的下标关系。
而这个虚拟数组的操作对于m+n为偶数也适用。
奇妙吧,这样下来,就可以把m+n的奇偶情况统一起来了。
下面是代码:
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m =nums1.size(),n=nums2.size();
if(m>n)
return findMedianSortedArrays(nums2,nums1);// 必须 让nums1的长度小于nums1否则的话m很大n很小,m二分的话会使得Nums2越界
int c1,c2;
int lo =-1,hi = 2*m; //我们这里的c1 c2表示切割的左半部分的最后一个数字的下标 所以c1的范围为[-1,2*m]
int lmax1 ,lmax2,rmin1,rmin2;
while(lo<hi)
//-1 6 c1=2 c2=0
{ //-1 2 c1=0,c2=2
c1=floor((lo+hi)/2.0); //因为二分范围的左边界是-1 为了使得-1/2结果为-1 所以要用floor函数
c2=m+n-2-c1; //因为(c1+1)+(c2+1)=总数的一半=(2*m+2*n)/2
lmax1 =c1<0?INT_MIN:nums1[c1/2]; //如果c1为-1的话,则nums1的切割全部属于右半边
lmax2 =c2<0?INT_MIN:nums2[c2/2];//同上
rmin1= c1>=2*m-1?INT_MAX:nums1[(c1+1)/2];//如果c1为2*m-1即最右边的话,nums1的切割全部属于左半边
rmin2= c2>=2*n-1?INT_MAX:nums2[(c2+1)/2];//同理
if(lmax1>rmin2)
hi=c1;
else if (lmax2>rmin1)
lo=c1+1;
else
break;
}
return (min(rmin1,rmin2)+max(lmax1,lmax2))/2.0;
}
};
那么我这道题苦想了三天的意义是什么?学到了什么新知识?获得了什么新技能?
都没有。
那还有什么意义呢?
有。
再遇难题,我必从容自若。