@LeetCode寻找两个有序数组的中位数--Median of Two Sorted Arrays[C++]
问题描述
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 。
请找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O ( l o g ( m + n ) ) O(log(m+n)) O(log(m+n))。
可以假设 nums1 和 nums2 不会同时为空。
示例1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3) / 2 = 2.5
解题方法及复杂度分析
递归法
在统计中,中位数被用来:
将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。
中位数的作用是用来划分数组,我们依据这个原理来分析。
首先,在 A 中任一位置划分,分成两个部分:
由于 A 中有 m m m 个元素,所以有 m + 1 m+1 m+1 种划分的方法 ( i = 0 i = 0 i=0 ~ m m m)。
可以知道:
l e n ( l e f t _ A ) = i len(left\_A) = i len(left_A)=i, l e n ( r i g h t _ A ) = m − i len(right\_ A) = m-i len(right_A)=m−i
注意:当 i = 0 i=0 i=0 时,left_A为空集,而当 i = m i = m i=m 时,right_A为空集。
采用同样的方式,在 B 中任一位置划分成两个部分:
将left_A 和 left_B 放入一个集合,并将 right_A 和 right_B 放入另一个集合。再把这两个新的集合分别命名为 left_part 和 right_part:
如果可以确认:
- l e n ( l e f t _ p a r t ) = l e n ( r i g h t _ p a r t ) len(left\_part) = len(right\_part) len(left_part)=len(right_part)
- m a x ( l e f t _ p a r t ) ⩽ m a x ( r i g h t _ p a r t ) max(left \_ part) \leqslant max(right\_ part) max(left_part)⩽max(right_part)
那么,我们已经将 {A, B} 中的所有元素划分为相同长度的两个部分,且其中一部分中的元素总是大于另一部分中的元素。那么:
m
e
d
i
a
n
=
m
a
x
(
l
e
f
t
_
p
a
r
t
)
+
m
i
n
(
r
i
g
h
t
_
p
a
r
t
2
median = \frac{max(left\_part)+min(right\_part}{2}
median=2max(left_part)+min(right_part
要确保这两个条件,只需要保证:
i + j = m − i + n − j ( 或 : m − i + n − j + 1 ) 如 果 n ⩾ m , 只 需 要 使 i ∈ [ 0 , m ] , j = m + n + 1 2 − i i+j=m-i+n-j(或:m-i+n-j+1) 如果n\geqslant m,只需要使i \in [0,m],\ j=\frac{m+n+1}{2}-i i+j=m−i+n−j(或:m−i+n−j+1)如果n⩾m,只需要使i∈[0,m], j=2m+n+1−i
ps.1 为了简化分析,假设A[ i − 1 i-1 i−1], B[ j − 1 j-1 j−1], A[ i i i], B[ j j j]总是存在,哪怕出现 i = 0 , i = m , j = 0 i=0,i=m,j=0 i=0,i=m,j=0,或是 j = n j=n j=n 这样的临界条件。我们在最后讨论临界值。
ps.2 为什么 n ⩾ m n\geqslant m n⩾m?由于 0 ⩽ i ⩽ m 0\leqslant i\leqslant m 0⩽i⩽m 且 j = m + n + 1 2 − i j=\frac{m+n+1}{2}-i j=2m+n+1−i,必须确保 j j j 不是负值。如果 n < m n < m n<m,那么 j j j 将可能是负数,而这会造成错误的答案。
所以,需要做的是:
在 [0, m m m] 中搜索并找到目标对象 i i i,以使:
B [ j − 1 ] ≤ A [ i ] 且 A [ i − 1 ] ≤ B [ j ] , 其 中 j = m + n + 1 2 − i B[j-1]\le A[i] 且A[i-1]\le B[j],其中j=\frac{m+n+1}{2}-i B[j−1]≤A[i]且A[i−1]≤B[j],其中j=2m+n+1−i
接着,按照以下步骤来进行二叉搜索:
- 设 imin = 0, imax = m m m,然后开始在[imin, imax]中进行搜索。
- 令 i = i m i n + i m a x 2 , j = m + n + 1 2 − i i=\frac{imin+imax}{2},\ j=\frac{m+n+1}{2}-i i=2imin+imax, j=2m+n+1−i
- 现在有
l
e
n
(
l
e
f
t
_
p
a
r
t
)
=
l
e
n
(
r
i
g
h
t
_
p
a
r
t
)
len(left\_ part)=len(right\_ part)
len(left_part)=len(right_part)。只会遇到三种情况:
– B [ j − 1 ] ≤ A [ i ] 且 A [ i − 1 ] ≤ B [ j ] B[j-1]\le A[i]\ 且\ A[i-1]\le B[j] B[j−1]≤A[i] 且 A[i−1]≤B[j]:这意味着找到目标对象 i i i,所以可以停止搜索。
– B [ j − 1 ] > A [ i ] B[j-1] > A[i] B[j−1]>A[i]:这意味着 A [ i ] A[i] A[i] 太小,必须调整 i i i 以使 B [ j − 1 ] ≤ A [ i ] B[j-1]\le A[i] B[j−1]≤A[i]。增大 i i i,这是因为当 i i i 被增大, j j j 就会被减小,因此B[ j − 1 j-1 j−1]会减小,而A[ i i i]会增加,那么 B [ j − 1 ] ≤ A [ i ] B[j-1]\le A[i] B[j−1]≤A[i] 就可能被满足。若减小 i i i,同理可分析无法满足 B [ j − 1 ] ≤ A [ i ] B[j-1]\le A[i] B[j−1]≤A[i]。所以,必须将搜索范围调整为 [ i + 1 [i+1 [i+1, imax]。因此,设 imin = i + 1 i+1 i+1,并转到步骤2。
– A [ i − 1 ] > B [ j ] A[i-1]>B[j] A[i−1]>B[j]:这意味着A[ i − 1 i-1 i−1]太大,必须减小 i i i 以使 A [ i − 1 ] ≤ B [ j ] A[i-1]\le B[j] A[i−1]≤B[j]。也就是说,必须将搜索范围调整为 [imin, i − 1 i - 1 i−1]。因此,设 imax = i − 1 i-1 i−1,并转到步骤2。
当找到目标对象 i i i 时,中位数为:
m a x ( A [ i − 1 ] , B [ j − 1 ] ) , 当 m + n 为 奇 数 时 max(A[i-1],B[j-1]),当m+n为奇数时 max(A[i−1],B[j−1]),当m+n为奇数时
m a x ( A [ i − 1 ] , B [ j − 1 ] ) + m i n ( A [ i ] , B [ j ] ) 2 , 当 m + n 为 偶 数 时 \frac{max(A[i-1],B[j-1])+min(A[i],B[j])}{2},当m+n为偶数时 2max(A[i−1],B[j−1])+min(A[i],B[j]),当m+n为偶数时
现在,考虑临界值 i = 0 , i = m , j = 0 , j = n i=0, \ i=m,\ j=0,\ j=n i=0, i=m, j=0, j=n,此时 A [ i − 1 ] , A [ i ] , B [ j − 1 ] , B [ j ] A[i-1],\ A[i],\ B[j-1],\ B[j] A[i−1], A[i], B[j−1], B[j]可能不存在。
首先需要确保
m
a
x
(
l
e
f
t
_
p
a
r
t
)
≤
m
i
n
(
r
i
g
h
t
_
p
a
r
t
)
max(left\_ part)\le min(right\_ part)
max(left_part)≤min(right_part)。因此,如果
i
i
i 和
j
j
j 不是临界值(这意味着
A
[
i
−
1
]
,
B
[
j
−
1
]
,
A
[
i
]
,
B
[
j
]
A[i-1],B[j-1],A[i],B[j]
A[i−1],B[j−1],A[i],B[j] 全部存在),那么必须同时检查
B
[
j
−
1
]
≤
A
[
i
]
B[j-1]\le A[i]
B[j−1]≤A[i] 以及
A
[
i
−
1
]
≤
B
[
j
]
A[i-1]\le B[j]
A[i−1]≤B[j] 是否成立。但是如果
A
[
i
−
1
]
,
B
[
j
−
1
]
,
A
[
i
]
,
B
[
j
]
A[i-1],\ B[j-1],\ A[i],\ B[j]
A[i−1], B[j−1], A[i], B[j] 中部分不存在,那么只需要检查这两个条件中的一个(或不需要检查)。所以,需要做的是:
在[0, m m m]中搜索并找到目标对象 i i i,以使:
( j = 0 o r i = m o r B [ j − 1 ] ≤ A [ i ] ) 或 是 ( i = 0 o r j = n o r A [ i − 1 ] ≤ B [ j ] ) , 其 中 j = m + n + 1 2 − 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 (j=0 or i=m or B[j−1]≤A[i]) 或是 (i=0 or j=n or A[i−1]≤B[j]),其中j=2m+n+1−i
在循环搜索中,只会遇到三种情况:
- ( j = 0 o r i = m o r B [ j − 1 ] ≤ A [ i ] ) 或 是 ( i = 0 o r j = n o r A [ i − 1 ] ≤ B [ j ] ) (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=0 or i=m or B[j−1]≤A[i]) 或是 (i=0 or j=n orA[i−1]≤B[j])
- i < m a n d B [ j − 1 ] > A [ i ] i < m\ and\ B[j-1]>A[i] i<m and B[j−1]>A[i],这意味着 i i i 太小,必须增大它。
- i > 0 a n d A [ i − 1 ] > B [ j ] i>0\ and\ A[i-1]>B[j] i>0 and A[i−1]>B[j],这意味着 i i i 太大,必须减小它。
ps. 为什么在遇到三种情况时不讨论 j j j 的情况呢?
m ≤ n , i < m ⟶ j = m + n + 1 2 − i > m + n + 1 2 − m ≥ 2 m + 1 2 − m ≥ 0 m\le n,\ i<m\longrightarrow j=\frac{m+n+1}{2}-i>\frac{m+n+1}{2}-m\ge \frac{2m+1}{2}-m\ge 0 m≤n, i<m⟶j=2m+n+1−i>2m+n+1−m≥22m+1−m≥0
m ≤ n , i > 0 ⟶ j = m + n + 1 2 − i < m + n + 1 2 ≤ 2 n + 1 2 ≤ n m\le n,\ i>0\longrightarrow j=\frac{m+n+1}{2}-i<\frac{m+n+1}{2}\le \frac{2n+1}{2}\le n m≤n, i>0⟶j=2m+n+1−i<2m+n+1≤22n+1≤n
所以, i < m ⟶ j > 0 i<m\longrightarrow j>0 i<m⟶j>0 以及 i > 0 ⟶ j < n i>0\longrightarrow j<n i>0⟶j<n 始终成立。
复杂度分析
- 时间复杂度:
O
(
l
o
g
(
m
+
n
)
)
O(log(m+n))
O(log(m+n))
– 因为算法需要二叉搜索A[ i i i]和B[ j j j],而 i ∈ [ 0 , m ] , j = m + n + 1 2 − i i\in [0,m],\ j=\frac{m+n+1}{2}-i i∈[0,m], j=2m+n+1−i,所以时间复杂度为 O ( l o g ( m + n ) ) O(log(m+n)) O(log(m+n))。
程序实现
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
{
int m = nums1.size();
int n = nums2.size();
if(m > n) {
vector<int> temp = nums1; nums1 = nums2; nums2 = 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 && nums2[j - 1] > nums1[i] {
iMin = i + 1;
}
else if(i > iMin && nums1[i - 1] > nums2[j]) {
iMax = i - 1;
}
else {
int maxLeft = 0;
if(!i) { maxLeft = nums2[j - 1]; }
else if(!j) { maxLeft = nums1[i - 1]; }
else { maxLeft = max(nums1[i - 1], nums2[j - 1]); }
if((m + n) % 2) { return maxLeft; }
int minRight = 0;
if(i == m) { minRight = nums2[j]; }
else if(j == n) { minRight = nums1[i]; }
else {minRight = min(nums2[j], nums1[i]); }
return (maxLeft + minRight) / 2.0;
}
}
return 0.0;
}
};
@ 山东·威海 2019.01.28