有两个有序数组nums1和nums2分别为m和n。
找到两个有序数组的中位数。 总运行时间复杂度应为O(log(m + n))。
您可以假设nums1和nums2不能都为空。
例 1:
nums1 = [1, 3]
nums2 = [2]
The median is 2.0
例2:
nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5
要解决这个问题,我们需要了解“中位数有什么用”。 在统计(statistics)中,中位数用于:
将一组分成两个相等长度的子集,一个子集总是大于另一个子集。
如果我们理解使用中位数来划分,我们非常接近答案。
首先让我们在随机位置 i i i 将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)。并且我们知道
len(left_A)=i,len(right_A)=m−i.
注意:当i=0时,left_A是空的,当i=m时,right_A是空的
同样,我们在随机位置 j j j 将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]
如果我们可以确定:
- len(left_part) = len(right_part)
- max(left_part) <= min(right_part)
然后,我们将{A, B}里面的所有元素划分成长度相等的两部分,并且一部分的元素总是大于另一部分的元素,接着 因此,我们需要这样做: python ##############################分割线############################## 由于所有的极端情况(the corner cases),这个问题很难实现(notoriously hard)。 大多数实现将奇数长度(odd-lengthed)和偶数长度(even-lengthed)的数组视为两种不同的情况并单独处理它们(treat them separately)。 事实上,带着一点点小技巧。 这两种情况可以合并为一种,从而形成一种非常简单的解决方案,其中(几乎)不需要特殊处理。 首先,让我们以一种略微不同寻常的方式(slightly unconventional way)看待“MEDIAN”的概念(concept)。 那是: 如果将一个有序数组切分成长度相等的两部分,然后median(中位数)就是 Max(lower_half)和Min(upper_half)的平均值 例如,对于[2 3 5 7],我们在3到5之间进行切割: 中位数median=(3+5)/2。请注意,我将使用’/'表示切割,(数字/数字)表示通过本文中的数字进行切割。 对于[2 3 4 5 6],我们通过4进行切割,如下所示: 由于我们将4分成两半,我们现在说下部和上部子数组都包含4.这个想法也也可以得到正确的答案:(4 + 4)/ 2 = 4; 为方便起见,我们使用L表示切割后的数字,R表示右边的数字。 例如,在[2 3 5 7]中,我们分别有L = 3和R = 5。 我们观察到L和R的索引与数组N的长度有以下关系: 不难得出index(L) =(N-1)/ 2,index®=N / 2。 因此,中位数可以表示为 为了准备好这两个数组的情况,让我们在数字之间添加一些虚构的“位置”(表示为#’),并将数字视为“位置”。 如您所见,无论长度N如何,总是有2 * N + 1个位置。因此,中间切口应始终在第N个位置(从0开始 0-based)。 由于在这种情况下index(L)=(N-1)/ 2和index(R)= N / 2,我们可以推断(infer)index(L)=(CutPosition-1)/ 2,index(R)=(CutPosition))/ 2。 现在针对双两个数组情况: 类似于一个数组(one-array)问题,我们需要找到一个将两个数组分成两半的分割 左半部分的任何元素 <= 右半部分的任何元素 我们也可以做如下观察: 在上面的例子中 现在怎样知道,这个分割是不是我们想要的分割呢?因为L1,L2是左半部分中最大的数,而R1,R2是右边的最小数,我们只需要 确保下半部分中的任何数<=上半部分中的任何数。 事实上,由于L1 <= R1和L2 <= R2自然得到保证,因为A1和A2是升序的,我们只需要确保:L1 <= R2和L2 <= R1。 现在我们可以使用简单的二叉搜索来找出结果。 如果L1 > R2,意味着A1的左边部分有很多较大的数,然后我们把A1的分割位置C1左移
为了满足这些条件,我们仅需要保证:
ps.1 为了简便,我假设A[i-1],B[j-1],A[i],B[j]都总是有效的,即使i=0,i=m,j=0,j=n,我将讨论如何处理这些边缘值。( edge values)
ps.2 为什么n>=m? 因为确保j是非负的,0<=i<=m和j=(m+n+1)/2 - i
如果n
我们可以按照下面描述的步骤进行二叉搜索(binary search):
javaclass 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;
}
}
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
方法2
[2 3 / 5 7]
[2 3 (4/4) 5 7]
N Index of L / R
1 0 / 0
2 0 / 1
3 1 / 1
4 1 / 2
5 2 / 2
6 2 / 3
7 3 / 3
8 3 / 4
median=(L + R)/2 = (A[(N-1)/2] + A[N/2])/2
[6 9 13 18] -> [# 6 # 9 # 13 # 18 #] (N = 4)
position index 0 1 2 3 4 5 6 7 8 (N_Position = 9)
[6 9 11 13 18]-> [# 6 # 9 # 11 # 13 # 18 #] (N = 5)
position index 0 1 2 3 4 5 6 7 8 9 10 (N_Position = 11)
A1: [# 1 # 2 # 3 # 4 # 5 #] (N1 = 5, N1_positions = 11)
A2: [# 1 # 1 # 1 # 1 #] (N2 = 4, N2_positions = 9)
[# 1 # 2 # 3 # (4/4) # 5 #]
[# 1 / 1 # 1 # 1 #]
L1 = A1[(C1-1)/2]; R1 = A1[C1/2];
L2 = A2[(C2-1)/2]; R2 = A2[C2/2];
L1 = A1[(7-1)/2] = A1[3] = 4; R1 = A1[7/2] = A1[3] = 4;
L2 = A2[(2-1)/2] = A2[0] = 1; R2 = A1[2/2] = A1[1] = 1;
L1 <= R1 && L1 <= R2 && L2 <= R1 && L2 <= R2
If we have L1 > R2, it means there are too many large numbers on the left half of A1, then we must move C1 to the left (i.e. move C2 to the right);
If L2 > R1, then there are too many large numbers on the left half of A2, and we must move C2 to the left.
Otherwise, this cut is the right one.
After we find the cut, the medium can be computed as (max(L1, L2) + min(R1, R2)) / 2;
如果L2 > R1,意味着A2的左边部分有很多较大的数,然后我们把A2的分割位置C2左移
否则,这个分割位置就是我们要找的分割位置,然后中位数就是median=(max(L1, L2) + min(R1, R2)) / 2;double findMedianSortedArrays(vector