题目
问题网址:
https://leetcode.com/problems/median-of-two-sorted-arrays/description/
问题描述:
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)).
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
解决方法
方法一:借助临时数组(以空间换时间)
主要思想:
这种解法的思想比较简单,主要是利用一个临时数组,先将两个有序的数组进行排序,然后判断数据总数目是奇数还是偶数,取相应的中位数。主要需要考虑的是数组的排序性能,数组排序的主要思想如下图:
实现代码:
#include <iostream>
#include <vector>
using namespace std;
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size(), n = nums2.size();
vector<int> temp;
int i = 0, j = 0;
//当数组nums1和nums2都不为空时,先加入小的数据,再加入大的数据
while((i < m) && (j < n)){
if(nums1[i] <= nums2[j]){
temp.push_back(nums1[i]);
++i;
}else{
temp.push_back(nums2[j]);
++j;
}
}
//若数组nums1和nums2有一个为空,则不需要判断上述过程,采用下面的解决方法
//直接将剩余的数组元素添加到temp
if(i < m){
for( int k = i; k < m; ++k){
temp.push_back(nums1[k]);
}
}
if(j < n){
for( int k = j; k < n; ++k){
temp.push_back(nums2[k]);
}
}
//对temp数组的大小分奇数和偶数讨论
if((m+n)%2==1){
return temp[(m+n-1)/2];
}else{
return (temp[(m+n)/2-1]+temp[(m+n)/2])/2.0;
}
}
int main()
{
vector<int> nums1, nums2;
nums1.push_back(2);
nums1.push_back(3);
nums2.push_back(6);
cout << findMedianSortedArrays(nums1, nums2) << endl;
return 0;
}
Submission Detail:
2080 / 2080 test cases passed.
Status: Accepted
Runtime: 69 ms
方法二:利用数据特点
主要思想:
注:本方法思想和代码主要来自Leetcode讨论区。
Step1:
(1)假设数组A在下标为i处被切分,如下:
left_A | right_A
A[0],A[1],...,A[i-1] | A[i], A[i+1], ..., A[m-1]
因数组A共有m个元素,所以数组有m+1种,即在下标0~m处切分都可以。若len(left_A) = i,则len(right_A) = m-i;若len(left_A) = 0,则len(right_A) = m。
(2)同样,B也是如此,
left_B | right_B
B[0],B[1],...,B[j-1] | B[j], B[j+1], ..., B[n-1]
(3)将 A A 和的左半部分和右半部分合并,如下图:
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]
(4)如我们可以保证:
i) len(left_part) == len(right_part)
ii) max(left_part) <= min(right_part)
则我们可将A+B分成相同长度的两部分。若总数据数目是奇数,则中位数是max(left_part)或者min(right_part),若总数据数目是偶数,则中位数是(max(left_part)+min(right_part))/2。
(5)可将问题转化为如下:
i) i + j = m - i + n - j(若总数目为奇数,则是m - i + n - j + 1)
若n > m, 我们只需要设置: i = 0 ~ m, j = (m + n + 1)/2 - i
ii) B[j-1] <= A[i] 或者 A[i-1] <= B[j]
(6)因上述涉及到A[i-1], B[j-1], A[i], B[j],所以需要对i=0, i=m, j=0, j=n这几个边界值进行讨论。
(i)上述设置n > m的原因是为了保证当0 <= i <= m时,j=(m + n + 1)/2 - i 的值非负。
(ii)问题转化为:
Searching i in [0, m], to find an object `i` that:
B[j-1] <= A[i] and A[i-1] <= B[j], ( where j = (m + n + 1)/2 - i )
(7)使用二分查找,可有如下分析:
<1> Set imin = 0, imax = m, then start searching in [imin, imax]
<2> Set i = (imin + imax)/2, j = (m + n + 1)/2 - i
<3> Now we have len(left_part)==len(right_part). And there are only 3 situations
that we may encounter:
<a> B[j-1] <= A[i] and A[i-1] <= B[j]
Means we have found the object `i`, so stop searching.
<b> B[j-1] > A[i]
Means A[i] is too small. We must `ajust` i to get `B[j-1] <= A[i]`.
Can we `increase` i?
Yes. Because when i is increased, j will be decreased.
So B[j-1] is decreased and A[i] is increased, and `B[j-1] <= A[i]` may
be satisfied.
Can we `decrease` i?
`No!` Because when i is decreased, j will be increased.
So B[j-1] is increased and A[i] is decreased, and B[j-1] <= A[i] will
be never satisfied.
So we must `increase` i. That is, we must ajust the searching range to
[i+1, imax]. So, set imin = i+1, and goto <2>.
<c> A[i-1] > B[j]
Means A[i-1] is too big. And we must `decrease` i to get `A[i-1]<=B[j]`.
That is, we must ajust the searching range to [imin, i-1].
So, set imax = i-1, and goto <2>.
当i确定后,中位数如下:
max(A[i-1], B[j-1]) (when m + n is odd)
or (max(A[i-1], B[j-1]) + min(A[i], B[j]))/2 (when m + n is even)
Step2:
考虑边界值情况:i=0, i=m, j=0, j=n。
我们需要讨论的情况如下:
Searching i in [0, m], to find an object `i` that:
(j == 0 or i == m or B[j-1] <= A[i]) and
(i == 0 or j == n or A[i-1] <= B[j])
where j = (m + n + 1)/2 - i
对在代码中可能遇到的问题,下面分类讨论:
<a> (j == 0 or i == m or B[j-1] <= A[i]) and
(i == 0 or j = n or A[i-1] <= B[j])
Means i is perfect, we can stop searching.
<b> j > 0 and i < m and B[j - 1] > A[i]
Means i is too small, we must increase it.
其实,i < m ⇒ j > 0:m <= n, i < m ==> j = (m+n+1)/2 - i > (m+n+1)/2 - m >= (2*m+1)/2 - m >= 0
<c> i > 0 and j < n and A[i - 1] > B[j]
Means i is too big, we must decrease it.
其实,i > 0 ⇒ j < n:m <= n, i > 0 ==> j = (m+n+1)/2 - i < (m+n+1)/2 <= (2*n+1)/2 <= n
实现代码:
#include <iostream>
#include <vector>
using namespace std;
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size(), n = nums2.size();
//下面程序的作用是保证数组nums1是短的那个,而nums2是长的那个
vector<int> tempArr;
int temp;
if(m > n){
tempArr = nums1;
nums1 = nums2;
nums2 = tempArr;
temp = m;
m = n;
n = temp;
}
int imin = 0, imax = m, half_len=(m+n+1)/2, max_of_left, min_of_right, i, j;
while(imin <= imax){
i = (imin + imax)/2; //i使用二分法的思想
j = half_len - i; //j的更新条件
if((i < m) && (nums2[j-1] > nums1[i])){ //非边界情况
imin = i + 1;
}else if((i > 0) && (nums1[i-1] > nums2[j])){ //非边界情况
imax = i - 1;
}else{
//考虑边界情况
if(i==0){
max_of_left = nums2[j-1];
}else if( j == 0){
max_of_left = nums1[i-1];
}else{
max_of_left = max(nums1[i-1], nums2[j-1]);
}
if((m+n)%2==1){ //奇数处理
return max_of_left;
}
if(i==m){ //偶数处理
min_of_right = nums2[j];
}else if(j ==n ){
min_of_right = nums1[i];
}else{
min_of_right = min(nums1[i], nums2[j]);
}
return (max_of_left + min_of_right)/2.0;
}
}
}
int main()
{
vector<int> nums1, nums2;
nums1.push_back(2);
nums1.push_back(3);
nums2.push_back(1);
nums2.push_back(6);
cout << findMedianSortedArrays(nums1, nums2) << endl;
return 0;
}
Submission Detail:
2080 / 2080 test cases passed.
Status: Accepted
Runtime: 47 ms