您的位置:首页 > 编程语言 > C语言/C++

[c++]LeetCode Median of Two Sorted Arrays问题

2015-10-18 08:54 381 查看
问题描述:

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)).

总结大牛的算法如下:

假设nums1,nums2的大小分别为m,n。

将有序系列nums1分为左右两个部分,有m中分法,如下所示为第i中分法:

| |
| |
| i
| |
|

| nums1[0] |
nums1[1] | nums1[2] |
nums1[3]...| nums1[i-1] |
nums1[i] ....|
nums1[m-2] | nums1[m-1] |

同样对nums2有n种分法,讲nums1,nums2左边和左边的序列组合为一个新的集合numsLeft,同样可以得到numsRight

如果能满足一下两个条件,就能容易的得到中位数:

numsLeft.size() == numsRight.size() 或者 numsLeft.size() == numsRight.size() +1
左边集合中的数 <= 右边集合中的数

假设nums1,nums2分别在i,j处分割,则要满足:

i+j=m-i+n-j or i+j=m-i+n-j+1
nums1[i-1] <= nums2[j] and nums2[j-1] <= nums1[i]

对于i==0 or m, j==0 or n的情况 i-1,j-1会超出数组索引范围,需要讨论,所以思路如下,i从0到m依次递增,得到nums1的一种分法,由最终左右两边数字的个数相等,得到对应的nums2的分割方法,比较如果满足nums1[i-1] <= nums2[j] and nums2[j-1] <= nums1[i],则找到了符合要求的分法,跳出,求中位数。

我们必须保证nums1.size()<=nums2.size(),这样先对一个小的数组分割,不管怎么分都能对大的数组分割满足左右个数相等的要求。
代码如下:
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
if(nums1.size() > nums2.size())
return findMedianSortedArrays(nums2,nums1);
decltype(nums1.size()) i=0,j=0,m=nums1.size(),n=nums2.size();

if(m==0)
if(n%2)
return nums2[n/2];
else
return (nums2[n/2-1]+nums2[n/2])/2.0;

for(i=0;i<=m;i++)
{
j=(n+m+1)/2-i;
if(i==0 || j==n)
if(nums1[i] >= nums2[j-1])
break;
else
continue;
else if(i==m || j==0)
if(nums1[i-1] <= nums2[j])
break;
else
continue;
else
{
if(nums1[i-1] <= nums2[j] && nums2[j-1] <=nums1[i])
break;
}
}
cout<<"i  "<<i<<endl;
cout<<"j  "<<j<<endl;
//left set
int numleft=0,numright=0;
if(i==0)
numleft=nums2[j-1];
else if(j==0)
numleft=nums1[i-1];
else
numleft=max(nums1[i-1],nums2[j-1]);

if((n+m)%2)
return numleft;
else
{
if(i==m)
numright=nums2[j];
else if(j==n)
numright=nums1[i];
else
{
numright=min(nums1[i],nums2[j]);
}
return (numleft+numright)/2.0;
}

}
};


说明:

对于从0开始编号的数组,如何取到中间的值? 如果m为奇数, nums[(m-1)/2],因为去尾的原因也可以直接写成nums[m/2],当m为偶数,是中间两个数的平均值 (nums[m/2]+nums[m/2-1])/2.0。
如果是从1开始编号的数组,m为奇数时应为nums1[(m+1)/2],当m为偶数时,为(nums[m/2]+nums[m/2+1])/2.0。
为什么j=(m+n)/2-i 或者 j=(m+n+1)/2-i,在程序中用j=(m+n+1)/2-i就可以完全表示,不需要分类讨论?因为当m+n为奇数时,左边要比右边多分一个数,j=(m+n+1)/2-i。当m+n为偶数时,(m+n+1)/2和(m+n)/2结果是一样的,所以不用分类讨论。
在3的问题上扩展,3/2==1, -3/2==-1,直接去尾
当nums1.size() > nums2.size()调换一下数组的位置即可
当m+n为奇数时,中间值出现在numsLeft,取max(nums1[i-1],nums2[j-1]),但是需要讨论边界情况,当m+n为偶数时,取numsLeft得最大值和numsRight的最小值,然后取平均即可,同样要考虑边界情况

以上的代码是i从0到m递增,一次验证分割方法是否满足要求,因为nums1,nums2都是有序数列,可以使用二分法进行搜索,修改如下:

class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
if(nums1.size() > nums2.size())
return findMedianSortedArrays(nums2,nums1);
decltype(nums1.size()) i=0,imin=0,imax=nums1.size(),m=imax,n=nums2.size(),j=0,half=(m+n+1)/2;

if(m == 0)
{
if(n%2)
return nums2[n/2];
else
return (nums2[n/2-1]+nums2[n/2])/2.;
}

while(imin <= imax)
{
i=(imin+imax)/2;
j=half-i;

if(i ==0 || j == n)
{
if(nums1[i] < nums2[j-1])
imin=i+1;
else break;
}
else if(j ==0 || i == m)
{
if(nums1[i-1] > nums2[j])
imax=i-1;
else break;
}
else
{
if(nums1[i-1] > nums2[j])
imax=i-1;
else if(nums2[j-1] > nums1[i])
imin=i+1;
else
break;
}
}
cout<<"i is  "<<i<<endl;
cout<<"j is  "<<j<<endl;

int numLeft=0,numRight=0;
if(i==0)
numLeft=nums2[j-1];
else if(j==0)
numLeft=nums1[i-1];
else
numLeft=max(nums1[i-1],nums2[j-1]);

if((n+m)%2)
return numLeft;
else
{
if(i==m)
numRight=nums2[j];
else if(j==n)
numRight=nums1[i];
else
numRight=min(nums1[i],nums2[j]);

return (numLeft+numRight)/2.;
}

}
};


最后放一个大神的代码吧:
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size(), n = nums2.size();
if (m > n) return findMedianSortedArrays(nums2, nums1);
int i, j, imin = 0, imax = m, half = (m + n + 1) / 2;
while (imin <= imax) {
i = (imin & imax) + ((imin ^ imax) >> 1);
j = half - i;
if (i > 0 && j < n && nums1[i - 1] > nums2[j]) imax = i - 1;
else if (j > 0 && i < m && nums2[j - 1] > nums1[i]) imin = i + 1;
else break;
}
int num1;
if (!i) num1 = nums2[j - 1];
else if (!j) num1 = nums1[i - 1];
else num1 = max(nums1[i - 1], nums2[j - 1]);
if ((m + n) & 1) return num1;
int num2;
if (i == m) num2 = nums2[j];
else if (j == n) num2 = nums1[i];
else num2 = min(nums1[i], nums2[j]);
return (num1 + num2) / 2.0;
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: