寻找两个有序数组的中位数
题目描述:
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
链接https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/
示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
思路:(官方题解)
由题目可知,给定两个有序数组,假设分别为数组A,数组B。
在任一位置将数组A划分成两个部分
left_A | right_A
A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]
同理在任一位置将数组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. $len(left_part) = len(right_part)$
2. $max(left_part)\le min(right_part)$
那么,我们已经将{A, B}中的所有元素划分为相同长度的两个部分,且其中一部分的元素总是大于另一部分的元素。那么:
$median = \frac{max(left_part)+min(right_part)}{2}$
确保这两个条件,我们只需要保证:
1. $i+j = m-i+n-j$(或:($m-i+n-j+1$))如果$n \ge m$,只需要使$i = 0 \sim m,j=\frac{m+n+1}{2}-i$
2. $B[j-1] \le A [i] 以及 A[i-1] \le B[j]$
接着,按照以下步骤进行二叉树搜索:
1.设$imin=0, imax=m$,然后开始在$\left|{imin, imax}\right|$中进行搜索。
2.令$i = \frac{imin+imax}{2}, j =\frac{m+n+1}{2} - i$
3.现在我们有$len(left_part)=len(right_part)$.我们会遇到三种情况:
- $B[j - 1]| \le A[i]$且$A[i-1]\le B[j]$
这意味着我们找到了目标对象$i$,所以可以停止搜索。- $B[j-1] > A[i]$:
这意味着$A[i]$太小,我们必须调整$i$以使$B[j-1] \le A[i]$
由$i$和$j$的关系可知,$i$增大的时候,$j$就会减小。所以此处必须增大$i$,将搜索范围调整为$[i + 1, imax]$。因此$imin=i+1$,并转向步骤2。- $A[i-1]>B[j]$。这说明$A[i-1]$太大,我们必须减小$i$以使$A[i - 1]\le B[j]$。也就是说,我们必须将搜索范围调整为$[imin, i-1]$。
因此,着$imax=i-1$,并转向步骤2.
当找到目标对象i时,中位数为:
$max(A{i-1],B[j-1]})$,当$m+n$为奇数时
$\frac{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)\le min(right_part)$。如果$i$和$j$不是临界值,必须同时检查$B[j-1]\le A[i]$以及$A[i-1] \le B[j]$是否成立。但是如果$A[i-1], B[j-1], A[i], B[j]$中部分不存在,我们只需要检查两个条件中的一个(或不需要检查)
在$[0, m]$中搜索目标对象$i$,以使:($j=0$ or $i=m$ or $B[j-1]\le A[i]$) 或是 ($i=0$ or $j=n$ or $A[i-1]\le B[j]$),其中$j=\frac{m+n+1}{2}-i$
循环搜索中,会遇到三种情况:
1.($j=0$ or $i=m$ or $B[j-1]\le A[i]$) 或是 ($i=0$ or $j=n$ or $A[i-1]\le B[j]$)这说明$i$是完美的,可以停止搜索。
2.$j>0$ and $i < m$ and $B[j-1] >A[i]$这意味着$i$太小,必须增大它。
3.$i>0$ and $j < n$ and $A[i-1]>B[j]$这意味着$i$太大,我们必须减小它。
JAVA版
class Solution {
public double findMedianSortedArrays(int[] A, int[] B) {
int m = A.length;
int n = B.length;
if (m > n) { // to ensure m<=n
int[] temp = A; A = B; B = temp;
int tmp = m; m = n; n = tmp;
}
int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
while (iMin <= iMax) {
int i = (iMin + iMax) / 2;
int j = halfLen - i;
if (i < iMax && B[j-1] > A[i]){
iMin = i + 1; // i is too small
}
else if (i > iMin && A[i-1] > B[j]) {
iMax = i - 1; // i is too big
}
else { // i is perfect
int maxLeft = 0;
if (i == 0) { maxLeft = B[j-1]; }
else if (j == 0) { maxLeft = A[i-1]; }
else { maxLeft = Math.max(A[i-1], B[j-1]); }
if ( (m + n) % 2 == 1 ) { return maxLeft; }
int minRight = 0;
if (i == m) { minRight = B[j]; }
else if (j == n) { minRight = A[i]; }
else { minRight = Math.min(B[j], A[i]); }
return (maxLeft + minRight) / 2.0;
}
}
return 0.0;
}
}
Python版
def median(A, B):
m, n = len(A), len(B)
if m > n:
A, B, m, n = B, A, n, m
if n == 0:
raise ValueError
imin, imax, half_len = 0, m, (m + n + 1) / 2
while imin <= imax:
i = (imin + imax) / 2
j = half_len - i
if i < m and B[j-1] > A[i]:
# i is too small, must increase it
imin = i + 1
elif i > 0 and A[i-1] > B[j]:
# i is too big, must decrease it
imax = i - 1
else:
# i is perfect
if i == 0: max_of_left = B[j-1]
elif j == 0: max_of_left = A[i-1]
else: max_of_left = max(A[i-1], B[j-1])
if (m + n) % 2 == 1:
return max_of_left
if i == m: min_of_right = B[j]
elif 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