Problem
There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
You may assume nums1 and nums2 cannot be both empty.
Example
num1 = [1, 3]
num2 = [2]
median = 2
#include <stdio.h>
int max(int a, int b)
{
return a > b ? a:b;
}
int min(int a, int b)
{
return a < b ? a:b;
}
double findMedianSortedArrays1(int* nums1, int nums1Size, int* nums2, int nums2Size) {
int t= (nums1Size + nums2Size + 1) / 2;
int i = 0, j = 0;
int res = 0;
while(t--) {
if(i >= nums1Size) {
res = nums2[j++];
}
else if(j >= nums2Size) {
res = nums1[i++];
}
else if(nums1[i] < nums2[j]) {
res = nums1[i++];
}
else {
res = nums2[j++];
}
}
//printf("%d\n", res);
int tol = nums1Size + nums2Size;
if(tol % 2 == 1)
return res;
int res2 = 0;
if(i >= nums1Size) {
res2 = nums2[j++];
}
else if(j >= nums2Size) {
res2 = nums1[i++];
}
else if(nums1[i] < nums2[j]) {
res2 = nums1[i];
}
else {
res2 = nums2[j];
}
//printf("%d\n", res2);
return (double)(res + res2)/2.0;
}
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
int *a, *b, n, m;
if(nums1Size <= nums2Size) {
a = nums1, b = nums2;
m = nums1Size, n = nums2Size;
}
else {
a = nums2, b = nums1;
m = nums2Size, n = nums1Size;
}
int low = 0, high = m;
int i, j;
while(low <= high) {
i = (low + high) / 2;
j = (m + n + 1) / 2 - i;
if(i > low && a[i - 1] > b[j]) {
high = i - 1;
}
else if(i < high && b[j - 1] > a[i]) {
low = i + 1;
}
else {
int maxleft;
if(i == 0) maxleft = b[j - 1];
else if(j == 0) maxleft = a[i - 1];
else maxleft = max(b[j - 1], a[i - 1]);
if((m + n) % 2 == 1) return maxleft;
int minright;
if(i == m) minright = b[j];
else if(j == n) minright = a[i];
else minright = min(a[i], b[j]);
return (maxleft + minright) / 2.0;
}
}
return 0.0;
}
int main()
{
int a[100], b[100];
int n, m;
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++)
scanf("%d", &a[i]);
for(int j = 0; j < m; j++)
scanf("%d", &b[j]);
double res = findMedianSortedArrays(a, n, b, m);
printf("%.1f\n", res);
return 0;
}
TIP
- 什么是中位数
中位数(Median)又称中值,统计学中的专有名词
是按顺序排列的一组数据中居于中间位置的数
如果观察值有偶数个,通常取最中间的两个数值的平均数作为中位数
在统计中,中位数被用来:
将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素
解题思路
理解了中位数的定义,我们就十分接近答案
方法一
数组是有序的,则可以采用归并的思想:
假设两个数组的规模为(m+n),每次从两个数组中取出剩下元素中较小的一个,一直取到第(m+n+1)/2次。
若(m+n)为奇数,则第(m+n+1)/2次所取的数即为答案;
若(m+n)为偶数,则需要再取一个数,与第(m+n+1)/2次所取的数的平均值即为答案
时间复杂度分析
- 时间复杂度:O(m+n)
- 空间复杂度:O(1)
方法二
利用中位数的性质:中位数左右两边的元素个数相等。
A数组里m个元素,所以有m+1中切分方法(i = 0~m)
left_A | right_A
A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]
len(left_num1) = i, len(right_num1) = m - i
注意:当i=0 时,left_A 为空集,而当i=m 时,right_A 为空集。
采用同样的方式,我们在任一位置将B划分成两个部分
left_B | right_B
B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
将left_A 和eft_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中所有的元素就被我们划分为长度相同的两个部分,且其中一部分中的元素总是大于另外一部分中的元素,接下来就可以套用公式
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=(0,m) \\ j = \frac{m+n+1}{2}-i i=(0,m)j=2m+n+1−i
- B[j - 1] <= A[i] && A[i - 1] <= B[j]
现在我们有len(left_part)=len(right_part)。 而且我们只会遇到三种情况
1.
B[j−1]≤A[i] 且 A[i−1]≤B[j],搜索结束
B[j−1]>A[i]:这意味着 A[i] 太小,我们必须调整i以使B[j−1]≤A[i]。
我们可以增大i吗?
是的,因为当 i被增大的时候,j 就会被减小。
因此B[j−1] 会减小,而 A[i] 会增大,那么B[j−1]≤A[i] 就可能被满足。
我们可以减小i吗?
不行,因为当i被减小的时候,jj 就会被增大。
因此B[j−1] 会增大,而A[i] 会减小,那么B[j−1]≤A[i] 就可能不满足。
所以我们必须增大 ii。也就是说,我们必须将搜索范围调整为[i+1,imax]。
因此,设imin=i+1,并转到步骤 2。
A[i−1]>B[j]:
这意味着A[i−1] 太大,我们必须减小 ii 以使 A[i−1]≤B[j]。
也就是说,我们必须将搜索范围调整为[imin,i−1]。
因此,设imax=i−1,并转到步骤 2。
时间复杂度分析
- 时间复杂度:O(log(min(m,n))),
首先,查找的区间是[0,m]。
而该区间的长度在每次循环之后都会减少为原来的一半。
所以,我们只需要执行log(m) 次循环。由于我们在每次循环中进行常量次数的操作,所以时间复杂度为 O(log(m))。
由于m≤n,所以时间复杂度是 O(log(min(m,n)))。 - 空间复杂度:O(1)