一.定义
首先将给定值key与字典中间位置上元素的关键码(key)比较,如果相等,则检索成功;
否则,若key小,则在字典前半部分中继续进行二分法检索;
若key大,则在字典后半部分中继续进行二分法检索。
这样,经过一次比较就缩小一半的检索区间,如此进行下去,直到检索成功或检索失败。
偶数个取中间2个其中任何一个作为中间元素
二.应用
There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
针对这个问题,可以考虑对数组1和数组2分别进行一次切割,但是要保证数组1和数组2切割后左边元素之和等于右边元素之和,这样才可以保证最后我们求得的是median。
为了更好的切割,需解决以下问题:
1.数组个数为奇数和偶数的问题:
偶数的话很好理解,以A[]={1,2,3,4}为例,切割为A={1,2/3,4},其中切割左边L=2,右边R=3;
奇数的话,可以这样理解,A={1,2,3},切割为A={1,2/2,3},其中切割左边为L=2,右边R=2;
共有的特征是:L=A[(N-1)/2],R=A[N/2]。
2.
保证数组1和数组2切割后左边元素之和等于右边元素之和的问题:
由于切割可以选在数组的最开始和最末尾,为了分析方便,我们可以将数组进行扩展。
A[]={1,2,3,4}扩展为A[]={#1#2#3#4#},其中N1=4,Npos1=4*2+1=9。
B[]={2,3,5}扩展成B[]={#2#3#5#},其中N2=3,Npos2=7。
所以总共有2*N1+2*N2+2,如果进行切割,两个切割占2个位置,剩下2*N1+2*N2个位置,左右相等的话,左右各有N1+N2个位置。
如果我们选择B的切割位置在C2=2(0 based),切割后为:B[]={#2/3#5#},那么A的切割位置就在C1=N1+N1-C2处,即C1=5。
那么A进行切割后为:A[]={#1#2#3/3#4#},可以看到分割后左边和为7(算上#,不算切割位置),右边和为7,。
切割完成后,找出满足要求的切割,这就需要用到二分法检索。用二分法检索前,思考下面问题:
1.如果L1>R2,说明数组1左边大的元素比较多,数组1的切割需要往左边移动,相应的数组2的切割就会往右边移动。
2.如果L2>R1,说明数组2左边大的元素比较多,数组2的切割需要往左边移动,相应的数组1的切割就会往右边移动。
3.如果L1<=R2与L2<=R1(注:由于数组已排序,故不需要考虑L1<R1,L2<R2),说明两个切割位置都是合理的,返回值为:
return double(max(L1,L2)+min(R1,R2))/2;
算法复杂度为o(log(min(m,n)))。
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int N1 = nums1.size();
int N2 = nums2.size();
if (N1 < N2) return findMedianSortedArrays(nums2, nums1); // Make sure A2 is the shorter one.
if (N2 == 0) return ((double)nums1[(N1-1)/2] + (double)nums1[N1/2])/2; // If A2 is empty
int lo = 0, hi = N2 * 2;
while (lo <= hi) {
int mid2 = (lo + hi) / 2; // Try Cut 2
int mid1 = N1 + N2 - mid2; // Calculate Cut 1 accordingly
double L1 = (mid1 == 0) ? INT_MIN : nums1[(mid1-1)/2]; // Get L1, R1, L2, R2 respectively
double L2 = (mid2 == 0) ? INT_MIN : nums2[(mid2-1)/2];
double R1 = (mid1 == N1 * 2) ? INT_MAX : nums1[(mid1)/2];
double R2 = (mid2 == N2 * 2) ? INT_MAX : nums2[(mid2)/2];
if (L1 > R2) lo = mid2 + 1; // A1's lower half is too big; need to move C1 left (C2 right)
else if (L2 > R1) hi = mid2 - 1; // A2's lower half too big; need to move C2 left.
else return (max(L1,L2) + min(R1, R2)) / 2; // Otherwise, that's the right cut.
}
return -1;
}