题目梗概:
找出两个已排序数组的中位数。
解题思路:
首先了解什么是中位数?中位数是指一组有序数据中居于中间位置的数。对于奇数个数据的一组有序数据而言,中位数是居于中间位置的数,如1、2、3、4、5中,中位数是3。对于偶数个数据的一组有序数据而言,中位数是居于中间位置的两个数的平均数,如2、4、6、8、10、12中,中位数为6和8的平均数7。
那么,对于两组有序数据
A
A
A和
B
B
B,如何求出它们共同的中位数呢?设
A
A
A的大小为
n
n
n,
B
B
B的大小为
m
m
m,那么我可以假设在
A
A
A中存在一个位置
i
i
i将
A
A
A分成两部分(
0
<
=
i
<
=
n
0<=i<=n
0<=i<=n),在
B
B
B中存在一个位置
j
j
j将
B
B
B分成两部分
(
0
<
=
j
<
=
m
)
(0<=j<=m)
(0<=j<=m),使得
A
A
A的左半部分
A
0
⋯
A
i
−
1
A_0{\cdots}A_{i-1}
A0⋯Ai−1的长度加
B
B
B的左半部分
B
0
⋯
B
j
−
1
B_0{\cdots}B_{j-1}
B0⋯Bj−1的长度等于
A
A
A的右半部分
A
i
⋯
A
n
−
1
A_i{\cdots}A_{n-1}
Ai⋯An−1的长度加
B
B
B的右半部分
B
j
⋯
B
m
−
1
B_j{\cdots}B_{m-1}
Bj⋯Bm−1的长度,即
l
e
n
(
A
0
⋯
A
i
−
1
+
B
0
⋯
B
j
−
1
)
=
l
e
n
(
A
i
⋯
A
n
−
1
+
B
j
⋯
B
m
−
1
)
len(A_0{\cdots}A_{i-1}+B_0{\cdots}B_{j-1})=len(A_i{\cdots}A_{n-1}+B_j{\cdots}B_{m-1})
len(A0⋯Ai−1+B0⋯Bj−1)=len(Ai⋯An−1+Bj⋯Bm−1),或
l
e
n
(
A
0
⋯
A
i
−
1
+
B
0
⋯
B
j
−
1
)
=
l
e
n
(
A
i
⋯
A
n
−
1
+
B
j
⋯
B
m
−
1
)
+
1
len(A_0{\cdots}A_{i-1}+B_0{\cdots}B_{j-1})=len(A_i{\cdots}A_{n-1}+B_j{\cdots}B_{m-1})+1
len(A0⋯Ai−1+B0⋯Bj−1)=len(Ai⋯An−1+Bj⋯Bm−1)+1,上述可以简化成
i
+
j
=
m
−
i
+
n
−
j
i+j=m-i+n-j
i+j=m−i+n−j或者
i
+
j
=
m
−
i
+
n
−
j
+
1
i+j=m-i+n-j+1
i+j=m−i+n−j+1,即
j
=
m
+
n
+
1
2
−
i
j=\frac{m+n+1}{2}-i
j=2m+n+1−i,其中
m
>
=
n
m>=n
m>=n,因为
0
<
=
i
<
=
n
0<=i<=n
0<=i<=n,则
j
=
m
+
n
+
1
2
−
i
>
=
m
+
n
+
1
2
−
n
=
>
m
−
n
+
1
2
j=\frac{m+n+1}{2}-i>=\frac{m+n+1}{2}-n=>\frac{m-n+1}{2}
j=2m+n+1−i>=2m+n+1−n=>2m−n+1,若
m
<
n
m<n
m<n,则
j
j
j的结果可能为负,不满足预期,同时有
A
[
i
−
1
]
<
=
B
[
j
]
、
A
[
j
−
1
]
<
=
B
[
i
]
A[i-1]<=B[j]、A[j-1]<=B[i]
A[i−1]<=B[j]、A[j−1]<=B[i]。
现在假设
i
i
i的大小为
n
/
2
n/2
n/2,则可能出现以下3种情况:
- A [ i − 1 ] > B [ j ] A[i-1]>B[j] A[i−1]>B[j],则 i i i过大,需要减少
- A [ j − 1 ] > B [ i ] A[j-1]>B[i] A[j−1]>B[i],则 i i i过小,需要增加
- A [ i − 1 ] < = B [ j ] A[i-1]<=B[j] A[i−1]<=B[j],满足情况,此时需要考虑 i = 0 i=0 i=0或者 j = 0 j=0 j=0或者 i = n i=n i=n或者 j = m j=m j=m的情况
解题算法:
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int n = nums1.length;
int m = nums2.length;
if(n>m){ // to ensure n<=m
int[] temp = nums1;
nums1 = nums2;
nums2 = temp;
int tmp = n;
n = m;
m = tmp;
}
int iMin = 0;
int iMax = n;
while(iMin<=iMax){
int i = (iMin + iMax) / 2;
int j = (n + m + 1) / 2 - i;
if(i<iMax && nums2[j-1]>nums1[i]){
iMin = i + 1; // i is too small
}else if(i>iMin && nums1[i-1]>nums2[j]){
iMax = i - 1; // i is too big
}else{ // i is perfect
int maxLeft = 0;
if(i==0){
maxLeft = nums2[j-1];
}else if(j==0){
maxLeft = nums1[i-1];
}else{
maxLeft = Math.max(nums1[i-1], nums2[j-1]);
}
if((m+n)%2==1){
return maxLeft;
}
int minRight = 0;
if(i==n){
minRight = nums2[j];
}else if(j==m){
minRight = nums1[i];
}else{
minRight = Math.min(nums1[i], nums2[j]);
}
return (maxLeft+minRight) / 2.0;
}
}
return 0.0;
}
}