leetcode之寻找两个有序数组的中位数c++解法
给定两个大小为 m 和 n 的 有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
解题思路:
在任意位置 i 将有m个元素的数组nums1划分成两部分:
left_nums1 | right_nums1
nums1[0],nums1[1],...,nums[i-1] | nums[i],nums1[i+1],...,nums[m-1]
同理,在任意位置 j 将有n个元素的数组nums2划分成两部分:
left_nums2 | right_nums2
nums1[0],nums1[1],...,nums[j-1] | nums[j],nums1[j+1],...,nums[n-1]
如果将left_nums1和left_nums2合并成left_part, right_nums1和 right_nums2合并成right_part后满足:
1. left_part.size() = right_part.size()
2. max(left_part) < min(right_part)
那么中位数即为:mid =(max(left_part) + min(right_part))/ 2
要确保满足这两个条件只需保证:
- i+j=m-i+n-j (或i+j=m-i+n-j+1)即 i = 0~m, j = (m+n+1)/2-i, (m<=n)
m<=n 是为了确保 j 不是负数,因为当m<=n时, (m+n+1)/2>=m,那么j=m-i>=0,
如果m>n,可能会出现 j = (m+n+1)/2-i<0的情况,从而导致错误。
- nums1[i-1]<nums2[j]
- nums2[j-1]<nums1[i]
所以,我们只需要在[0, m]中找到满足以上条件的 i 即可。采用以下步骤进行二叉树搜索:
3. 设imin=0,imax=m,然后在[imin,imax]中进行搜索
4. 令i = (imin+imax)/2,则 j = (m+n+1)/2
5. 此时已经满足第一个条件:两边长度相等或者左边比右边多一个元素(i+j=m-i+n-j+1,即左边i+j个数,右边(i+j-1)个数),也因此,当m+n为奇数时,中位数在左边。接下来会遇到三种情况:
- nums1[i-1]>nums2[j]:意味着 i 太大,要左移,搜索 i 的范围缩小到[imin,i-1]
- nums2[j-1]>nums1[i]:意味着 i 太小,要右移, 搜索 i 的范围缩小到[i+1, imax]
- nums1[i-1]<nums2[j] 且 nums2[j-1]<nums1[i]:意味着找到目标 i ,可以停止搜索
找到 目标 i 之后,中位数为:
max(nums1[i-1],nums2[j-1]),当m+n为奇数时
(max(nums1[i-1],nums2[j-1])+ min(nums1[i]+nums2[j]))/ 2,当m+n为偶数时
另外,还有几个临界值需要考虑:
- i = 0: 此时不存在nums[ i-1],所以不需要判断nums1[i-1]<nums2[j]是否成立,只需要判断nums1[i]>nums2[j-1]是否成立,如果不成立,则意味着 i 太小,要右移, 搜索 i 的范围缩小到[i+1, imax],如果成立,意味着 i 是完美的,我们可以停止搜索
- i = m: 此时不存在nums[i],所以不需要判断nums1[i]>nums2[j-1]是否成立,只需要判断nums1[i-1]<nums2[j]是否成立,如果不成立,则意味着 i 太大,要左移, 搜索 i 的范围缩小到[imin, i-1],如果成立,意味着 i 是完美的,我们可以停止搜索
- j = 0:此时不存在nums2[j-1]
- j = n:此时不存在nums2[j]
综上所述,在循环搜索中只会遇到以下3种情况:
- j>0且i<m且nums1[i]<nums2[j-1],这意味着 i 太小,我们必须增大它 (实际上,i<m⟹j>0始终成立,所以不需检查j>0是否成立)
- i>0且j<n且nums1[i-1]>nums2[j],这意味着 i 太大,我们必须减小它。( 实际上,i>0⟹j<n始终成立,所以不需要检查j<n是否成立 )
- 1和2都不满足,即(j=0或i=m或nums1[i]>nums2[j-1])或(i=0或j=n或nums1[i-1]<nums2[j]),意味着 i 是完美的,我们可以停止搜索
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
if(m>n){
nums1.swap(nums2);
int t = m; m = n; n= t;
}
int imin=0,imax=m,halflen=(m+n+1)/2;
while(imin<=imax){
int i = (imin+imax)/2;
int j = halflen-i;
if(i<imax&&nums2[j-1]>nums1[i])
imin = i+1;
else if(i>imin&&nums1[i-1]>nums2[j])
imax = i-1;
else{
int maxLeft = 0;
if(i==0)
maxLeft = nums2[j-1];
else if(j==0)
maxLeft = nums1[i-1];
else
maxLeft = max(nums2[j-1],nums1[i-1]);
if((m+n)%2==1) //当m+n为奇数时,中位数是左边的最大值
return maxLeft;
int minRight = 0;
if(i==m)
minRight = nums2[j];
else if(j==n)
minRight = nums1[i];
else
minRight = min(nums2[j],nums1[i]);
return (maxLeft+minRight)/2.0; //除以2.0使得结果为浮点数,如果除以整型2,那么3/2结果将是1,从而导致错误
}
}
return 0.0;
}
};