4. 寻找两个正序数组的中位数
给定两个大小为 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
解题
对两个数组进行二分求中位数:
关键点
中位数的位置一定是两个数的长度和的一半——故将两个数组看作整体;
得到中位数左边的数的个数——medium=(n1+n2)/2;{防止奇偶问题,优化为(n1+n2+1)/2};
划边界时,若划到nums1[i],则nums2必划到medium-i;
二分
对i进行二分查找,即满足边界左边的两个数都小于边界右边的两个数;
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
//视情况交换nums1nums2,使nums1短于nums2
if(nums1.size()>nums2.size()){
vector<int> tmp=nums2;
nums2=nums1;
nums1=tmp;
}
int n1=nums1.size();
int n2=nums2.size();
int n=(n1+n2+1)/2; //左边的个数
//从第一个数组的0-m找分割线
int left=0;
int right=n1;
while(left<right){
int i=left+(right-left+1)/2; //右取中位数,使得i-1不会越界
int j=n-i;
if(nums1[i-1]>nums2[j]){
right=i-1;
} else left=i;
}
int i=left;
int j=n-i;
int nums1left= i==0? INT_MIN:nums1[i-1];
int nums1right= i==n1? INT_MAX:nums1[i];
int num2left= j==0?INT_MIN:nums2[j-1];
int num2right= j==n2?INT_MAX:nums2[j];
if((n1+n2)%2) return max(nums1left,num2left);//奇数
else return max(nums1left,num2left)/2.0+min(nums1right,num2right)/2.0;
}
private:
double res;
};
从小往大的二分
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
if(nums1.size()>nums2.size()){
vector<int> tmp=nums2;
nums2=nums1;
nums1=tmp;
}
//将短的数组放到nums1
int n1=nums1.size();
int n2=nums2.size();
//找中位数——则左边的个数为一半
int Midieum=(n1+n2+1)/2; //找到中位数右边的下标,即左边的个数
//对n1进行二分,找到边界线
int left=0;
int right=n1;
while(left<right){
int i=left+(right-left+1)/2; //因为left=i,防止右边取不到,故取上底
int j=Midieum-i; //j为左边的个数-n1的个数,即nums2边界右边的下标
if(nums1[i-1]>nums2[j])//短数组边界左边大于长数组边界右边,往左边找
right=i-1;
else left=i;
}
//出来后,left=right=边界i
int i=left; //边界右边的位置;
int j=Midieum-i; //nums2边界右边的位置
int nums1leftmax= i==0? INT_MIN: nums1[i-1];
int nums1rightmin= i==n1? INT_MAX:nums1[i];
int nums2leftmax= j==0?INT_MIN:nums2[j-1];
int nums2rightmin= j==n2?INT_MAX:nums2[j];
if((n1+n2)%2) return max(nums1leftmax,nums2leftmax);
else return max(nums1leftmax,nums2leftmax)/2.0+min(nums1rightmin,nums2rightmin)/2.0;
}
};
从大往小的二分
int left=0;
int right=n1;
while(left<right){
int i=left+(right-left)/2;
int j=Midieum-i;
if(nums1[i]<nums2[j-1]) //往右边走
left=i+1;
else right=i;
}
关键点
将两个数组看作块分析,二分划线位置;

本文详细介绍了如何在O(log(m + n))的时间复杂度内找到两个正序数组nums1和nums2的中位数。通过二分法确定中位数位置,避免奇偶问题,优化为(n1+n2+1)/2,并分析了二分查找的不同策略,包括从小往大和从大往小的二分方法。

被折叠的 条评论
为什么被折叠?



