[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(),这样先对一个小的数组分割,不管怎么分都能对大的数组分割满足左右个数相等的要求。
代码如下:
说明:
对于从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都是有序数列,可以使用二分法进行搜索,修改如下:
最后放一个大神的代码吧:
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; } };
相关文章推荐
- 剑指offer第十八题【二叉树的镜像】c++实现
- Item 41:隐式接口与编译期多态 Effective C++笔记
- 关于C语言中Implicit declaration of function ‘XXXX’ is invalid in C99警告
- <<C++标准程序库>>中的STL简单学习笔记
- C++代码静态分析工具splint
- 基础c注意点(一)
- 浅析C++中String到WString的转换
- 浅析C++中String到WString的转换
- C++Study 指针详解
- [OOD-More C++ Idioms] 律师与委托人 (Attorney-Client)
- C语言 基础篇 printf
- 指针小谈.指针的值
- VS2010开发.cpp与.c的注意事项
- leetcode笔记:Minimum Depth of Binary Tree
- leetcode笔记:Maximum Depth of Binary Tree
- 对于for循环一些问题的解析
- c语言 数据类型 常量 变量 关键字 标示符命名范围
- C语言的分支语句解析
- C语言创建一个数组,利用函数调用实现数组的初始化,逆置和清零
- C语言中的隐式转换