Example 1:
nums1 = [1, 3]
nums2 = [2]
The median is 2.0
Example 2:
nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5
classSolution {
public:
doublefindMedianSortedArrays(vector<int>& nums1, vector<int>&nums2) {
}
};
自己汇编的程序不符合时间复杂度O(log(n+m)),而是O(nlgn)。
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
vector<int> Result(nums1);
for (auto &c : nums2)
Result.push_back(c);
sort(Result.begin(), Result.end());
if (Result.size() % 2 == 0)
return (double)(Result[Result.size() / 2 - 1] + Result[Result.size() / 2 - 1]) / 2;
else
return (double)Result[Result.size() / 2];
}
};
我们都知道二分查找的时间复杂度为O(logn),证明如下:
证明:二分查找的基本思想是将n个元素分成大致相等的两部分,去a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;如果x<a[n/2],则只要在数组a的左半部分继续搜索x,如果x>a[n/2],则只要在数组a的右半部搜索x.
时间复杂度无非就是while循环的次数!总共有n个元素,渐渐跟下去就是n,n/2,n/4,....n/2^k,其中k就是循环的次数由于你n/2^k取整后>=1,即令n/2^k=1
可得k=log2n,(是以2为底,n的对数)所以时间复杂度可以表示O()=O(logn);
1)我们将数组A剪成两部分
left_A | right_A
A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]
因为A有m个元素,因此有m+1中分段可能,我们知道 len(left_A) = i, len(right_A)= m - i . 当i = 0 , left_A为空, 当 i = m , right_A为空。.
2)同样我们对B也做该处理
left_B | right_B
B[0], B[1], .., B[j-1] | B[j], B[j+1], ..., B[n-1]
3)将left_A 和 left_B 看成一个数组,同时将right_A 和 right_B 当成另一个.我们分别命名为left_part何right_part.
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]
4)如果我们能确认
1) len(left_part) == len(right_part)
2) max(left_part) <= min(right_part)
那么我们将所有在{A,B}中的元素,分成两个相等的部分,左边的部分的元素全部小于右边部分的元素,中间值为median= (max(left_part) + min(right_part))/2.0
5)为了确保这两个情况,我们需要确定
(1) 2(i + j)==m+n或者2(i + j)=m+n+1整理如下:
i + j == m - i + n - j (or: m - i + n - j + 1)
if n >= m, 我们设置i = 0 ~ m, j = (m + n + 1)/2 – i
(2) B[j-1] <= A[i] and A[i-1] <= B[j]
在满足(1)的情况下满足(2)则i被找到,当i被找到返回值情况见7);
PS:1. 我假设 A[i-1],B[j-1],A[i],B[j] 总是有效的,即使 i=0,i=m,j=0,j=n ,我将会讨论如何处理这些边界问题。
PS:2. 为什么假设n>=m,是因为必须确认j是一个正数,由于0 <= i<= m,j = (m + n + 1)/2 - i. 如果n<m, j可能为负数,,会导致结果错误。
所以寻找i必须在0~m之间寻找,i满足 j = (m + n + 1)/2 - i 时,B[j-1] <= A[i] 和 A[i-1] <= B[j],
6)在这面我们可以做一个简单的程序流程“口述”
1、设置 imin=0,imax=m;
2、设置i = (imin + imax)/2,j = (m + n + 1)/2 – i;//i和j的索引分别是数组AB的中间索引+1
3、现在我们有len(left_part)==len(right_part),这里可能出现三种情况;
<a>B[j-1] <= A[i]and A[i-1] <= B[j]//这意味着我们找到了i停止搜索
<b> B[j-1] > A[i]//说明A[i]较小,我们必须调整i使得:B[j-1] <= A[i]
如何调整i的值:
PS1:能增加i的值吗?可以,因为增加i的值,这时j将减小,i+j的和不变,这时B[j-1] 是递减,A[i]是递增,这样可能会满足B[j-1] <= A[i],这时找到i
PS2:能减小i的值吗?显然不可能,因为A[i]已经足够小了,i减小,j增大,这时B[j-1]是递增,A[i]是递减,不可能满足B[j-1] <= A[i]。这种情况在程序中舍弃
小结:因此只能递增imin = i+1, 然后执行2;
<c> A[i-1] > B[j]
说明A[i-1]是大的.必须递减i的值,才会满足A[i-1]<=B[j],这样我们调整i的范围 [imin, i-1]。因此设置 imax = i-1,,然后接着执行2
7)当i被找到时,中间值为:
当m+n是奇数时: max(A[i-1], B[j-1])
当m+n是偶数时: (max(A[i-1], B[j-1]) + min(A[i], B[j]))/2
8)现在我们考虑边界情况, i=0,i=m,j=0,j=n 时 A[i-1],B[j-1],A[i],B[j] 不存在. 我们需要确保的是max(left_part) <= min(right_part),因此如果i和j不是边界值, A[i-1],B[j-1],A[i],B[j] 存在,我们必须检查 B[j-1] <= A[i]和A[i-1] <= B[j]情况,但是如果 A[i-1],B[j-1],A[i],B[j] 不存在,则不需要检查 B[j-1] <= A[i]和A[i-1] <= B[j]。例如,如果j=0, A[i-1] 不存在, 我们不需要检查 A[i-1] <= B[j]。因此我们需要做的是:在 [0, m]寻找i,当j = (m + n + 1)/2 – i时
(j ==0 or i == m or B[j-1] <= A[i])以及 (i == 0or j == n or A[i-1]<= B[j])
9)在寻找i的时候发生跳转只会发生下面三种情况:
<a> (j == 0 ori == m or B[j-1] <= A[i])和 (i == 0 or j = n or A[i-1]<= B[j])
说明我们找到了i,停止搜索
<b> j > 0和 i< m及 B[j- 1] > A[i]
说明i较小,这时增加i
<c> i > 0 andj < n and A[i - 1] > B[j]
说明i较大递减i
PS:因为 i < m ==> j > 0 and i > 0 ==> j < n .证明如下:
m <= n, i < m ==> j = (m+n+1)/2 - i > (m+n+1)/2 - m >= (2*m+1)/2 - m >= 0
m <= n, i > 0 ==> j = (m+n+1)/2 - i < (m+n+1)/2 <= (2*n+1)/2 <= n
因此在<b>和 <c>中,我们不需要考虑,是否j > 0 及j < n.
综上自己修正的代码如下:
class Solution1 {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2){
int m = nums1.size(), n = nums2.size(), max_of_left = 0, min_of_right = 0;
if (m > n) return findMedianSortedArrays(nums2, nums1);
int i, j, imin = 0, imax = m, half = (m + n + 1) / 2;
while (imin <= imax)
{
i = (imin + imax) / 2, j = half - i;
if (i > 0 && nums1[i - 1] > nums2[j])
imax = i - 1; //i太大,应该递减
else if (j > 0 && nums2[j - 1] > nums1[i])
imin = i + 1; //i太小,应该递增
else
break;
}
//如果i找到后
if (i == 0) max_of_left = nums2[j - 1];
else if (j == 0) max_of_left = nums1[i - 1];
else max_of_left = max(nums1[i - 1], nums2[j - 1]);
if ((m + n) % 2)
return max_of_left;
if (i == m) min_of_right = nums2[j];
else if (j == n) min_of_right = nums1[i];
else min_of_right = min(nums1[i], nums2[j]);
return (max_of_left + min_of_right) / 2.0;
}
};
如有错误,请大家指正。