蓝雨原创,转载请注明出处。contact author: qinglanyu_jun@foxmail.com
There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the meidan of the two sorted arrays. The overall run time complexity should
O
(
l
o
g
(
m
+
n
)
)
O(log(m+n))
O(log(m+n)).
You may assume nums1 and nums2 cannot be both empty.
Example 1:
nums1 = [1, 3]
nums2 = [2]
The median is 2.0
Example 2:
nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5
Solution 1 合并选择
最容易想到的办法,将两个有序数组进行合并,取最中间的数(合并后奇数个元素)或者最中间两个数(合并后偶数个元素)的平均数即可。其中合并后元素的个数是可以计算的,因此只需要找出合并后的中间元素即可,无需开辟空间存储合并后的数组,最多两个元素的空间用来存储遍历到的最后两个元素。
Code
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
unsigned m = nums1.size(), n = nums2.size();
unsigned last = (m+n)/2+1;
unsigned i=0, j=0;
int meidans[2] = {0};
unsigned idx = 0;
while(idx < last)
{
if(i<m && j<n)
{
meidans[idx&1] = (nums1[i]<=nums2[j]) ? (nums1[i++]) : (nums2[j++]);
}
else if(i<m)
{
meidans[idx&1] = (nums1[i++]);
}
else if(j<n)
{
meidans[idx&1] = (nums2[j++]);
}
idx ++;
}
return (double)(((m+n)&1) ? (meidans[(idx+1)&1]) : ((meidans[0]+meidans[1])/2.0));
}
};
执行结果:通过
执行用时:16ms,在所有C++提交中击败了84.66%的用户
内存消耗:7.3MB,在所有C++提交中击败了100.00%的用户
然而以上方法的时间复杂度为 O ( m + n ) O(m+n) O(m+n),达不到 O ( l o g ( m + n ) ) O(log(m+n)) O(log(m+n))的要求。
Solution 2 切分数组
设两有序数组分别为 A = { A [ 0 ] , A [ 1 ] , . . . , A [ m ] } A = \{A[0], A[1], ..., A[m]\} A={A[0],A[1],...,A[m]}和 B = { B [ 0 ] , B [ 1 ] , . . . , B [ n ] } B = \{B[0], B[1], ..., B[n]\} B={B[0],B[1],...,B[n]},现将 A A A和 B B B都拆分为左右两部分即:
A
=
A
l
e
f
t
+
A
r
i
g
h
t
A = A_{left} + A_{right}
A=Aleft+Aright,
A
l
e
f
t
=
{
A
[
0
]
,
A
[
1
]
,
.
.
.
,
A
[
i
−
1
]
}
A_{left} = \{A[0], A[1], ..., A[i-1]\}
Aleft={A[0],A[1],...,A[i−1]},
A
r
i
g
h
t
=
{
A
[
i
]
,
A
[
i
+
1
]
,
.
.
.
,
A
[
m
]
}
A_{right} = \{A[i], A[i+1], ..., A[m]\}
Aright={A[i],A[i+1],...,A[m]}
且
B
=
B
l
e
f
t
+
B
r
i
g
h
t
B = B_{left} + B_{right}
B=Bleft+Bright,
B
l
e
f
t
=
{
B
[
0
]
,
B
[
1
]
,
.
.
.
,
B
[
j
−
1
]
}
B_{left} = \{B[0], B[1], ..., B[j-1]\}
Bleft={B[0],B[1],...,B[j−1]},
B
r
i
g
h
t
=
{
B
[
j
]
,
B
[
j
+
1
]
,
.
.
.
,
B
[
n
]
}
B_{right} = \{B[j], B[j+1], ..., B[n]\}
Bright={B[j],B[j+1],...,B[n]}
此时,令
L
=
A
l
e
f
t
+
B
l
e
f
t
L = A_{left} + B_{left}
L=Aleft+Bleft
R
=
A
r
g
i
h
t
+
B
r
i
g
h
t
R = A_{rgiht} + B_{right}
R=Argiht+Bright
若
m
+
n
m+n
m+n即
A
A
A和
B
B
B的总长度为偶数时,条件
s
i
z
e
(
L
)
=
s
i
z
e
(
R
)
size(L) =size(R)
size(L)=size(R)
m
a
x
(
L
)
≤
m
i
n
(
R
)
max(L) \leq min(R)
max(L)≤min(R)
满足,那么所寻找的中位数为:
m
e
d
i
a
n
=
m
a
x
(
L
)
+
m
i
n
(
R
)
2
median = \frac{max(L)+min(R)}{2}
median=2max(L)+min(R)
若
m
+
n
m+n
m+n即
A
A
A和
B
B
B的总长度为奇数时,条件
s
i
z
e
(
L
)
=
s
i
z
e
(
R
)
+
1
size(L) =size(R)+1
size(L)=size(R)+1
m
a
x
(
L
)
≤
m
i
n
(
R
)
max(L) \leq min(R)
max(L)≤min(R)
满足,那么所寻找的中位数为:
m
e
d
i
a
n
=
m
a
x
(
L
)
median = max(L)
median=max(L)
又由于上述两组条件等价于以下三个条件:
条件一:
i
+
j
=
(
m
−
i
)
+
(
n
−
j
)
⇒
i
+
j
=
⌊
m
+
n
+
1
2
⌋
i+j=(m-i) + (n-j) \Rightarrow i+j=\lfloor\frac{m+n+1}{2}\rfloor
i+j=(m−i)+(n−j)⇒i+j=⌊2m+n+1⌋ ,当
m
+
n
m+n
m+n为偶数时
i
+
j
=
(
m
−
i
)
+
(
n
−
j
)
+
1
⇒
i
+
j
=
⌊
m
+
n
+
1
2
⌋
i+j=(m-i) + (n-j) + 1 \Rightarrow i+j=\lfloor\frac{m+n+1}{2}\rfloor
i+j=(m−i)+(n−j)+1⇒i+j=⌊2m+n+1⌋,当
m
+
n
m+n
m+n为奇数时
条件二:
假设
m
≤
n
m \leq n
m≤n,且
0
≤
i
≤
m
,
0
≤
j
≤
n
0 \leq i \leq m, 0\leq j\leq n
0≤i≤m,0≤j≤n,
则对于
∀
i
∈
[
0
,
m
]
\forall i \in[0, m]
∀i∈[0,m],都有
j
=
(
⌊
m
+
n
+
1
2
⌋
−
i
)
∈
[
0
,
n
]
j=(\lfloor\frac{m+n+1}{2}\rfloor-i)\in[0,n]
j=(⌊2m+n+1⌋−i)∈[0,n]
条件三:
B
[
j
−
1
]
≤
A
[
i
]
B[j-1]\leq A[i]
B[j−1]≤A[i]
A
[
i
−
1
]
≤
B
[
j
]
A[i-1]\leq 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]总是存在 。对于 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 [ − 1 ] = B [ − 1 ] = − ∞ A[-1]=B[-1]=-\infty A[−1]=B[−1]=−∞ , A [ m ] = B [ n ] = ∞ A[m]=B[n]=\infty A[m]=B[n]=∞即可。 因为其计算并不会影响 L L L和 R R R中的最大值或最小值产生影响。
最后程序需要计算的是:
在[0,m]中找到i,使得:
B[j-1]<=A[i]且A[i-1]<=B[j],其中j=(m+n+1)/2-i
同时上述条件也等价于
在[0,m]中找到最大的i,使得:
A[i-1]<=B[j],其中j=(m+n+1)2-i
因为:
当
i
i
i从
0
∼
m
0\sim m
0∼m 递增时,
A
[
i
−
1
]
A[i-1]
A[i−1]递增,
B
[
j
]
B[j]
B[j]递减,所以一定存在一个最大的
i
i
i满足
A
[
i
−
1
]
≤
B
[
j
]
A[i-1]\leq B[j]
A[i−1]≤B[j];
而对于最大的
i
i
i,有
i
+
1
i+1
i+1不满足,即代入
i
+
1
i+1
i+1则有
A
[
i
]
>
B
[
j
−
1
]
A[i]>B[j-1]
A[i]>B[j−1],也就是
B
[
j
−
1
]
<
A
[
i
]
B[j-1]<A[i]
B[j−1]<A[i]。
综上所述,在区间 [ 0 , m ] [0,m] [0,m]上对 i i i进行二分查找 ,找到最大的满足 A [ i − 1 ] ≤ B [ j ] A[i-1]\leq B[j] A[i−1]≤B[j]的 i i i值,得到了正确的划分有序数组 A A A和 B B B为有序数组左 L L L右 R R R两部分的方法。
Code
class Solution {
#define MAX(a, b) ((a)>(b)?(a):(b))
#define MIN(a, b) ((a)<(b)?(a):(b))
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
if (nums1.size() > nums2.size()){
return findMedianSortedArrays2(nums2, nums1);
}
unsigned m = nums1.size(), n = nums2.size();
int left = 0, right = m;
int maxL = 0, minR = 0;
while (left <= right){
// L = nums1[0, .., i-1], nums2[0, .., j-1]
// R = nums1[i, .., m-1], nums2[j, .., n-1]
int i = (left + right) / 2;
int j = (m + n + 1) / 2 - i;
int nums_i_1 = (i == 0 ? INT_MIN : nums1[i - 1]); // nums1[i-1]
int nums_i = (i == m ? INT_MAX : nums1[i]); // nums1[i]
int nums_j_1 = (j == 0 ? INT_MIN : nums2[j - 1]); // nums2[j-1]
int nums_j = (j == n ? INT_MAX : nums2[j]); // nums2[j]
if (nums_i_1 <= nums_j){
maxL = MAX(nums_i_1, nums_j_1);
minR = MIN(nums_i, nums_j);
left = i + 1;
}
else{
right = i - 1;
}
}
return ((m + n) & 1) == 0 ? (maxL + minR) / 2.0 : maxL;
}
执行结果:通过
执行用时:16ms,在所有C++提交中击败了84.42%的用户
内存消耗:7.1MB,在所有C++提交中击败了68.98%的用户
以上方法的时间复杂度为
O
(
l
o
g
m
i
n
(
m
,
n
)
)
O(log min(m,n))
O(logmin(m,n)),其中
m
m
m和
n
n
n另是数组
n
u
m
s
1
nums1
nums1和
n
u
m
s
2
nums2
nums2的长度。
空间复杂度为
O
(
1
)
O(1)
O(1)。