这是一道Hard的题,确实是难诶,感觉就是道数学题,像高考数学求导的那道大题一样,各种情况和边界条件需要考虑,还需要推导。自己没写出来,
主要参考Solution: https://leetcode.com/problems/median-of-two-sorted-arrays 和discuss:https://leetcode.com/problems/median-of-two-sorted-arrays/discuss/2481/Share-my-O(log(min(mn))-solution-with-explanation
他们的思路是一样的,不过一个是java一个是python而已。
详细的可以去看原文,这里只整理思路。
首先是根据中位数的定义,将问题等价为了
left_part | right_part
A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1] B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
用i,j去分割AB两个数组,如果上面的分割可以得到中位数,那么有
1) len(left_part) == len(right_part) or
len(right_part)+1 (人为规定如果odd的话leftpart大一点,这个无所谓,leftpart大的话就从leftpart找结果就行)
2) max(left_part) <= min(right_part)
那么even: median = (max(left_part) + min(right_part))/2 ;odd: median=left_part[-1]
上面两条件等价:
1) i+j = m-i+n-j or m-i+n-j+1
2) A[i-1]<=B[j] and B[j-1]<=A[i]
其中对于1)我们可以约束m<=n,那么有 j = (m+n+1)//2-i 。于是:
1) j = (m+n+1)//2-i s.t.m<=n
2) A[i-1]<=B[j] and B[j-1]<=A[i]
在条件1)下,有了i我们就有了j,于是我们在[0,m]里对i进行二分查找。(为什么是左闭右闭呢,因为i其实是一个位置,而不是一个元素,比如i=0,代表在元素0之前分割,i=m,代表在元素m之前(元素m不存在,等价与元素m-1之后分割))
对于2)i=0时,A[i-1]不存在;类似的,j=0,i=m,j=n时,B[j-1],A[i],B[j]不存在,所以这个边界条件我们也要处理一下。
i=0时,说明我们对i的搜索已经进行完毕,A组数据都应该在rightpart,那么i就是我们要的i了,至于结果怎么样要看是odd还是even了;
i=m时,A组数据都应该在leftpart,那么i就是我们要的i了;
j类似。
所以,i什么时候小呢?
B[j-1]>A[i]&&j!=0&&i!=m
等价于 B[j-1]>A[i]&&j>0&&i<m
i什么时候大呢?
A[i-1]>B[j]&&i!=0&&j!=n
等价于 A[i-1]>B[j]&&i>0&&j<n
剩下的时候就是i合适了。
但是这时i=0,j=0,i=m,j=n这几个特殊情况会影响我们的公式,所以要特殊处理。
照这个思路写了一版,并不对,先放这儿吧。
class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { int m=nums1.length,n=nums2.length; //ensure m<=n orelse j=(m+n+1)/2-i might be a negative interger ,that's not what we want if(m>n) { int[] temp=nums1;nums1=nums2;nums2=temp; int tempi = m;m=n;n=tempi; } if(m==0) { if(n%2==0) return (nums2[n/2-1]+nums2[n/2])/2.0; else return (double)nums2[n/2]; } //i,j represents where we divide nums1,nums2 to two equivalent parts or len(left)==len(right)+1 int i=0,j=(m+n+1)/2-i; //binary search int pl=0,pr=m; while(pl<=pr) { i=(pl+pr)/2; j=(m+n+1)/2-i; int leftmax,rightmin; if(j>0&&i<m&&nums2[j-1]>nums1[i]) { //i is too small pl=(pl+pr)/2+1; } else if(i>0&&j<n&&nums1[i-1]>nums2[j]) { //i is too big pr=(pl+pr)/2-1; } else { //i,j are what we want if(i==0) leftmax=nums2[j-1]; else if(j==0) leftmax=nums1[i-1]; else leftmax=Math.max(nums1[i-1],nums2[j-1]); if(i==m) rightmin=nums2[j]; else if(j==n) rightmin=nums1[i]; else rightmin = Math.max(nums1[i], nums2[j]); if((m+n)%2==0) return (leftmax+rightmin)/2.0; else return (double)leftmax; } } return 0.0; } }
[-1,3]