寻找两个有序数组的中位数
问题描述
给定两个大小为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
思路1:归并加查找
这里以c/c++语言来具体化
- 首先看到题眼有序数组,还是两个有序数组去找中位数,笔者第一反应考虑到归并排序中的归并
- 归并之后在选取中间的数即为中位数(偶数为两数之和,奇数为中间数)
输入:nums1=[1,3,5]
nums2=[1,4,7,8]
输出:4
归并排序中归并部分(具体可以参考归并排序)
具体思路开始(可以参考下面几幅图)
- 建立str3[]数组,长度为len1+len2
- 将nums1[]和nums2[]的内容从小到大排列到新数组str3
- 找出str3中的中位数
代码
/*归并加查找*/
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int len1=nums1.size();
int len2=nums2.size();
int *str3=new int[len1+len2];
int i=0,j=0,k=0,l=0;
while(i <len1&&j<len2)/*开始归并*/
{
if(nums1[i]< nums2[j])
str3[k++]=nums1[i++];
else
str3[k++]=nums2[j++];
}
if(i<len1)
{
for(l=0;l<len1-i;l++)
str3[k+l]=nums1[i+l];
}
if(j<len2)
{
for(l=0;l<len2-j;l++)
str3[k+l]=nums2[j+l];
}
int n=(len1+len2)/2;/*开始查找*/
if(((len1+len2)&1)==1)//如果n是奇数
return (double)strs3[n];
else
return (double)(str3[n-1]+str3[n])/2;
}
};
根据图片可以知道中位数:奇数为str3[len/2],偶数为str3[len/2-1]和str3[len/2]加起来的平均值
思路2:二分查找
中位数理解:
将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。
思路2主要使用了二分查找方法(具体实现点击这里),时间复杂度为o(logn)(可结合下图)
- 引入虚拟加入#来扩充数组,解决两个数组加起来不是偶数的问题(虚拟加入只是假象,并没有将原数组进行改变),将原数组元素虚拟各为2n+1个和2m+1个,总和为2n+2m+2
输入:nums1=[1,3,5]
nums2=[1,4,7,8]
假想:nums1=[#,1,#,3,#,5,#]
nums2=[#,1,#,4,#,7,#,8,#]
- 引入分割点,c1和c2,查找中位数为c1遍历nums1的所有可能,也就是七种可能,如果c1为#即为两数之间,c1为数字,即左半边和右半边都为次数,分成两个部分,两个部分都有这个数
- 引入i和j来对数组二分查找,对数组上下限low,high进行操作
- 设置四个标记点LMax1,LMax2,RMin1,RMin2用于判断二分查找点左右
中位数即为左边区域最大的数和右边区域最小的数总和的平均值
(max(LMax1,LMax2)+min(RMin1,RMin2))/2.0
代码
/*哈希表*/
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int n=nums1.size();
int m=nums2.size();
if (n>m) //保证数组1一定最短
{return findMedianSortedArrays(nums2,nums1);
int LMax1,LMax2,RMin1,RMin2,c1, c2,low=0,high=2*n;
//我们目前是虚拟加了'#'所以数组1是2*n长度,数组2是2*m长度
while (low<=high) //二分
{
c1=(low+high)/2; //c1是二分的结果
c2=m+n-c1;//中位数作用,分成两堆要保证每一对数量相等,且一堆所有的数都大于另一堆数
//设置四个标记点,用于判断循环
LMax1=(c1==0)?INT_MIN:nums1[(c1-1)/2];
RMin1=(c1==2*n)?INT_MAX:nums1[c1/2];
LMax2=(c2==0)?INT_MIN:nums2[(c2-1)/2];
RMin2=(c2==2*m)?INT_MAX:nums2[c2/2];
if(LMax1>RMin2)
high=c1-1;
else if (LMax2>RMin1)
low=c1+1;
else
break;
}
return (max(LMax1,LMax2)+min(RMin1,RMin2))/2.0;//最后的2.0是强制转化为double类型
}
};
二分查找更为简单,而且能够满足时间复杂度要求
如果本文对宝宝打开思路有帮助,可以点个赞哦~