lintcode 两个排序数组的中位数
两个排序的数组A和B分别含有m和n个数,找到两个排序数组的中位数,要求时间复杂度应为O(log (m+n))。
给出数组A = [1,2,3,4,5,6] B = [2,3,4,5],中位数3.5
给出数组A = [1,2,3] B = [4,5],中位数 3
题目链接:http://www.lintcode.com/zh-cn/problem/median-of-two-sorted-arrays/#
这道题的难度等级为困难,难就难在要求时间复杂度为O(logn),那就不能使用普通的遍历来做,那样时间复杂度为O(n)。
首先在随机位置将A分成两部分:
left_A | right_A
A [0],A [1],...,A [i-1] | A [i],A [i + 1],...,A [m-1]
由于A有m个元素,所以有m + 1种切割(i = 0〜m)。其中:left_A.size() = i,right_A.size() = m-i。注意:当i = 0时,left_A为空,当i = m时,right_A为空。
同样,在随机位置将B切成两部分:
left_B | right_B
B [0],B [1],...,B [j-1] | B [j],B [j + 1],...,B [n-1]
将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]
如果我们可以确保:
1)left_part.size() == right_part.size()
2)max(left_part)<= min(right_part)
那么我们将{A,B}中的所有元素划分为两个长度相等的部分,一个部分总是大于另一个部分。然后中值=(max(left_part)+ min(right_part))/ 2。
为了确保这两个条件,只需要确保:
(1)i + j == m-i + n-j(或:m-i + n-j + 1)
如果n> = m,我们只需要设置:i = 0〜m,j =(m + n + 1)/ 2-i
(2)B [j-1] <= A [i],A [i-1] <= B [j]
为什么一定要n> = m?因为必须确保j是合法索引,因为0 <= i <= m和j =(m + n + 1)/ 2-i。如果n <m,则j可以是负数,这将导致错误的结果。
在[0,m]中搜索i,找到一个切分点i(j =(m + n + 1)/ 2-i):
使得B [j-1] <= A [i]和A [i-1] <= B[j]。
我们可以按照以下步骤进行搜索:
<1>设置imin = 0,imax = m,然后开始搜索[imin,imax]
<2>设置i =(imin + imax)/ 2,j =(m + n + 1)/ 2-i
<3>现在left_part.size() == right_part.size()。而且只有3种情况:
(1) B[j-1] <= A [i]和A [i-1] <= B [j]
意味着找到了切分点i,停止搜索。
(2) B[j-1]> A [i]
意味着A [i]太小。必须调整i得到B [j-1] <= A [i]。而i只能增加不能减小,因为当i减小时j将增加,因此,B [j-1]增加并且A [i]减小,永远不会满足。所以必须增加i。也就是说,调整搜索范围为[i + 1,imax]。因此,imin = i + 1和然后回到第<2>步。
(3) A [i-1]> B [j]
意味着A [i-1]太大,必须减少i得到A [i-1] <= B [j]。因此,设置imax = i-1然后回到第<2>步。
当找到切分点i时,中值为:
max(A [i-1],B [j-1])(当m + n是奇数时)
或(max(A [i-1],B [j-1])+ min(A [i],B [j]))/ 2(当m + n为偶数时)
现在考虑边值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]都存在),我们必须检验B [j-1] <= A [i],A [i-1] <= B [j]。但是如果A [i-1],B [j-1],A [i],B [j]中的一些不存在,则不需要检查这两个条件中对应的一个。例如,如果i = 0,则A [i-1]不存在,则不需要检查A [i-1] <= B [j]。所以,需要做的是:
在[0,m]中搜索i,找到一个切分点i,使得:
(j == 0或i == m或B [j-1] <= A [i])和
(i == 0或j == n或A [i-1] <= B [j])
其中j =(m + n + 1)/ 2-i
在搜索循环中,只会有三种情况:
(1)(j == 0或i == m或B [j-1] <= A [i])和
(i == 0或j = n或A [i-1] <= B [j])
满足条件停止搜索。
(2) j> 0和i <m并且B [j-1]> A [i]
i太小,增加i。
(3)i> 0和j <n并且A [i-1]> B [j]
i太大,减少i。
说的比较多,下面是代码:
class Solution {
public:
/**
* @param A: An integer array.
* @param B: An integer array.
* @return: a double whose format is *.5 or *.0
*/
double findMedianSortedArrays(vector<int> A, vector<int> B) {
//int minidx = 0, maxidx = m, i, j, num1, mid = (m + n + 1) >> 1,num2;
if (A.size() > B.size()) return findMedianSortedArrays(B,A);
int m = A.size(),n = B.size();
int imin = 0,imax = m,half_len = (m + n + 1) / 2,i,j,max_of_left,min_of_right;
while (imin <= imax) {
i = (imin + imax) / 2;
j = half_len - i;
if (i < m && j > 0 && B[j - 1] > A[i]) imin = i + 1;
else if (i > 0 && j < n && B[j] < A[i - 1]) imax = i - 1;
else {
if (i == 0) max_of_left = B[j - 1];
else if (j == 0) max_of_left = A[i - 1];
else max_of_left = max(A[i - 1],B[j - 1]);
break;
}
}
if ((m + n) % 2 == 1) return max_of_left;
if (i == m) min_of_right = B[j];
else if (j == n) min_of_right = A[i];
else min_of_right = min(A[i],B[j]);
return (max_of_left + min_of_right) / 2.0;
}
};