算法1(并非最优解):可以将 median 转化为第k小的数of two sorted arrays。(此处k为泛指,下面k为两个array size和)
如果array size为双数,median为 (第k/2小+第(k/2+1)小)/2。For example, array size = 4, median = (第2小+第3小)/2.
如果array size为单数,median为 第(k/2+1)小。For example, array size = 5, median = 第3小。
如何找到第k小的数:
Recursion
假设两个array的size都不小于k/2,所以nums1[k/2-1] and nums2[k/2-1]都存在。我们先比较nums1[k/2-1] and nums2[k/2-1] (-1 is the offset for the 0-based index), 我们会得到如下3种情况:
1.nums1[k/2-1] < nums2[k/2-1]
如果有如上不等式,我们可以肯定的是nums1[k/2-1] < 第k小的数。证明1: 在现有的条件下,我们让nums1[k/2-1]尽量的大。nums1[k/2-1]前面有k/2-1个数,nums2[k/2-1]相同,所以nums1[k/2-1] and nums2[k/2-1]前面共有2*(k/2-1) = k-2个数。然后已知nums1[k/2-1] < nums2[k/2-1],还要让nums1[k/2-1]尽量的大,nums2[k/2-1]也要尽量的大。所以nums2[k/2-1]最大就为第k小的数,nums1[k/2-1]最大就为第k-1小的数,nums1[k/2-1]还是小于第k小的数。证明2(反证法): 假设nums1[k/2-1] > 第k小的数,假设其为第k+1小的数。由于nums1[k/2-1] < nums2[k/2-1], nums2[k/2-1]至少是(k+2)最小的数。nums1[k/2-1]前面有k/2-1个数,nums2[k/2-1]相同,所以nums1[k/2-1] and nums2[k/2-1]前面共有2*(k/2-1) = k-2个数。我们可以得到nums1[k/2-1]前面最多只有k-2个数,这与nums1[k/2-1]是第k+1最小矛盾。
2.nums2[k/2-1] < nums1[k/2-1]
同上论证我们可得nums2[k/2-1] < 第k小的数
3.nums1[k/2-1] == nums2[k/2-1]
因为nums1[k/2-1] and nums2[k/2-1]前面共有2*(k/2-1) = k-2个数,nums1[k/2-1] and nums2[k/2-1] 就是k-1小和第k小的数。
如何handle其中一个array size 不足k/2的情况:其实去除前面的假设(两个array的size都不小于k/2), generalize 所有的情况我们发现nums1[index1] and nums2[index2] 前面共有k-2个数是之前三条结论成立的最重要条件,所以我们只要保证index1+index2 = k就行了。
我们可以用以上的3种情况完成recursion。
Base Case (Note: i = nums1已经check完的个数; j = nums2已经check完的个数)
从recursion我们可以看出i在不断的递增并且会比j先达到它的上线,还有就是k不断的被除以2最后变为1。
1.nums1.size()=i, nums1已经全部check完,第k小数只会在nums2中出现: return nums2[j+k-1]
2.k=1, return min(nums1[i], nums2[j])
最后别忘了handle k的奇偶性对最后答案的影响。
现在讲的这种方法是O(log(k)),上code (并非最优解)
class Solution {
public:
double findKth(vector<int> &nums1, int i, vector<int> &nums2, int j, int k){
//make sure nums1.size()<nums2.size()
if(nums1.size()-i>nums2.size()-j)
return findKth(nums2, j, nums1, i, k);
//base case
if(nums1.size()==i) return nums2[j+k-1]; //i,j is the offset here, so we can also see it as nums2[k-1]
if(k==1) return min(nums1[i], nums2[j]); //min(nums1[i+1-1], nums2[j+1-1]);
//recursion
int pa = min(i+k/2, int(nums1.size())), pb = j+k-pa+i; //want k-pa, j is the offset, we need to add i back since pa include i
if(nums1[pa-1]<nums2[pb-1])
return findKth(nums1, pa, nums2, j, k-pa+i);
else if(nums1[pa-1]>nums2[pb-1])
return findKth(nums1, i, nums2, pb, k-pb+j);
else return nums2[pb-1];
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int total = nums1.size() + nums2.size();
if(total%2==1)
return findKth(nums1, 0, nums2, 0, total/2+1);
else
return (findKth(nums1, 0, nums2, 0, total/2)+findKth(nums1, 0, nums2, 0, total/2+1))/2;
}
};
attention:
Line 14: no matching function for call to 'min(int, std::vector<int>::size_type)'
solution: int(nums1.size())
最优解:log(min(n,m))
上面的算法分了两种情况来考虑,odd length and even length。但其实我们可以直接用binary search直到最后一步再用考虑奇偶性。为了方便理解,我们先考虑所有array length为偶数。在这一前提下,“median” 的定义:我们把一串数切成两段,然后median就是前半段数的最大值和后半段数的最小值的平均数。举例,[2 3 5 7] -> [2 3 / 5 7],平均数为3和5的平均数。定义前半段数的最大值为L,后半段数最大值为R。[2 3 4 5]中L=3 and R=5。
以上都是讨论一条array的情况,那么对于两条array来说,其实就是用两个cut将两条array这个整体分成数量相等的两份,median为前半段最大数和后半段最小数的平均值(前半段所有数都小于后半段都有数)。因为前后两段的数量要相等,只要一个array在哪里切决定后,第二个array在哪里切也就确定了。在这个前提下,我们只需要用binary search遍历短的那条array就行了。
跟上一解相同,我们用一个if来保证nums1.length<nums2.length。因为要用binary search,我们需要有两个pointer,这里为cutL and cutR。在一开始我们让 cutL = 0 and cutR = nums1.size(),while loop的condition为cutL<=cutR。cut1为nums1 cut的地方,相同cut2为nums2 cut的地方。因为我们要遍历nums1, cut1 = (cutR-cutL)/2+cutL。cut1定下来了,随之cut2 = len/2-cut1因为我们需要cut1+cut2=len/2。然后我们就需要得到L1, L2, R1 and R2。因为cut1 and cut2是1 based index, 所以我们需要-1。然后L1 = nums1[cut1-1] and L2 = nums2[cut2-1]。因为有-1,cut1 and cut2 不能小于0。有了L1 and L2, 我们也可以得出R1 = nums1[cut1] and R2 = nums2[cut2],并且cut1 and cut2最大为nums1.size() and nums2.size() respectively。
cut1和cut2能保证两条array分成的前后两部分数量相同,但是并不满足前半段所有数都小于后半段的所有数。这就可以成为我们推进遍历的条件。我们需要保证L1<R1, L1<R2, L2<R1, L2<R2。因为这两条array是排序好的,我们可以得到L1<R1 and L2<R2,所以只需要保证L1<R2 and L2<R1。
if L1>R2, 说明cut1需要往左(小)移 -> cutR = cut1-1
if L2>R1, 说明cut2需要往左(小)移,相对的cut1需要往右(大)移 -> cutL = cut1+1
当median的条件都满足了后,我们考虑总长度的奇偶性来决定最后的median
偶数:前半段最大数和后半段最小数的平均数
奇数:后半段最小数
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
if(nums1.size() > nums2.size()){
return findMedianSortedArrays(nums2, nums1);
}
int len = nums1.size()+nums2.size();
int cut1 = 0;
int cut2 = 0;
int cutL = 0;
int cutR = nums1.size();
while(cutL<=cutR){
cut1 = (cutR-cutL)/2+cutL;
cut2 = len/2-cut1;
cout << cut1<<" "<<cut2<<endl;
double L1 = (cut1 == 0) ? INT_MIN : nums1[cut1 - 1];
double L2 = (cut2 == 0) ? INT_MIN : nums2[cut2 - 1];
double R1 = (cut1 == nums1.size()) ? INT_MAX : nums1[cut1];
double R2 = (cut2 == nums2.size()) ? INT_MAX : nums2[cut2];
if(L1>R2){
cutR = cut1 - 1;
}
else if(L2>R1){
cutL = cut1 + 1;
}else{
if(len%2==0){
L1 = L1>L2 ? L1:L2;
R1 = R1<R2 ? R1:R2;
return (L1+R1)/2;
}
else{
R1 = R1<R2 ? R1:R2;
return R1;
}
}
}
return -1;
}
};
Lisa 呕心沥血为您打造
有些地方可能有点啰嗦,但是还是详尽为主~~~
Reference: http://www.cnblogs.com/grandyang/p/4465932.html
https://leetcode.com/problems/median-of-two-sorted-arrays/discuss/2471/very-concise-ologminmn-iterative-solution-with-detailed-explanation
https://blog.csdn.net/chen_xinjia/article/details/69258706