题目
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)).
You may assume nums1 and nums2 cannot be both empty.
Example 1:
nums1 = [1, 3]
nums2 = [2]
The median is 2.0
Example 2:
nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5
解法一
相对比较好想的方法,开辟一个新数组,将两个数组合并为一个有序数组,之后根据合并后的长度是偶数还是奇数,返回所对应的中位数
代码实现
//时间复杂度:O(m+n)
//空间复杂度:辅助数组O(m+n)
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size();
int m = nums2.size();
//不可能都是空数组
if (n == 0)
{
if (m % 2 == 0)
{
return (nums2[m / 2] + nums2[m / 2 - 1]) / 2.0;
}
return nums2[m / 2];
}
if (m == 0)
{
if (n % 2 == 0)
{
return (nums1[n / 2] + nums1[n / 2 - 1]) / 2.0;
}
return nums1[n / 2];
}
int count = 0,i=0,j=0;
vector<int> v(m + n, 0);
while (count != m + n)
{
if (i == n)
{
while (j != m)
{
v[count++] = nums2[j++];
}
break;
}
if (j == m)
{
while (i != n)
{
v[count++] = nums1[i++];
}
break;
}
if (nums1[i] >= nums2[j])
{
v[count++] = nums2[j++];
}
else
{
v[count++] = nums1[i++];
}
}
if (count % 2 == 0)
{
return (v[count / 2 - 1] + v[count / 2]) / 2.0;
}
return v[count / 2];
}
};
解法二
我们并不需要真正的开辟数组,我们只要遍历对应中位数次数之后,则必然最后一次遍历的结果就是中位数,在这里我们需要区分的是长度是偶数和奇数的情况
如果总长度是奇数的话,那么必然需要遍历的次数是(m+n)/2+1次,例如数组长度分别是4和5,则需要遍历的次数是5次,也就是(4+5)/2+1,如果总长度是偶数的话,遍历的次数是(m+n)/2+1次,例如数组长度分别是4,4,则需要遍历的次数是5次,因为偶数情况下中位数为中间两个数和的一半,所以我们还是需要遍历(m+n)/2+1次数,又因为数组下标从0开始,所以在代码中,次数是(m+n)/2
并且为了统一处理总长度是偶数和奇数的情况我们分别定义两个变量,分别用来记录本次遍历和上次遍历值,对于奇数,则返回的是最新的,对于偶数我们需要将这两个值和的一半返回
代码实现
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size();
int m = nums2.size();
int aStart = 0, bStart = 0;
int mid = (m + n) / 2;
int oldvalue = 0, newvalue = 0;
for (size_t i = 0; i <= mid; i++)
{
oldvalue = newvalue;
if (aStart < n && (bStart >= m || nums1[aStart] < nums2[bStart]))
{
newvalue = nums1[aStart++];
}
else
{
newvalue = nums2[bStart++];
}
}
if ((m + n) % 2 == 0)
{
return (oldvalue + newvalue) / 2.0;
}
else
return newvalue;
}
};
解法三
题目要求的时间复杂度是O(log(m+n)),采用上面两种解法虽然也可以ac过,但是效率较低,看到这种时间复杂度,很明显只有用到二分的方法才能达到,题目中的求中位数,其实是在求第k小数的一种特殊情况,而求第k小数有一种算法
上面的两种解法,我们一次便利相当于去掉不可能是中位数的一个值,也就是一个一个排除,由于数列是有序的,其实我们完全可以一半一半的排除,假设我们要找第k小数,我们可以每次循环排除掉k/2个数,我们一起看下面的例子
例子:
假设我们要找第七小的数字,
我们比较两个数组的第k/2个数字.如果k是奇数,向下取正,也就是比较第三个数字,上面数组中的8和下边数组中的3那个下,就表明该数组的前k/2个数字都不可能是第k小数字,所以可以排除,也就是1,2,3这三个数字不可能是第7小的数字,我们可以把它排除掉,将1389和45678910两个数组作为新的数组进行比较。
橙色的部分表示已经去掉的数字
由于我们已经去除掉3个数字,就是这3个数字一定在最前边,所以在两个新数组中,我们只需要找第7-3=4小的数字就可以了,也就是k=4.此时两个数组,比较第2个数字,3<5,所以我们可以把小的那个数组中的1,3排除掉了
我们又排除掉2个数字,所以现在找第4-2=2小的数字就可以了,此时比较两个数组中的第k/2=1个数,4=4,两个数相等,去掉任意一个都可以,在这里我们将下边的4去掉
由于又去掉一个数字,此时我们要找第1小的数字,所以只需判断两个数组中第一个数字那个小就可以了,也就是4,即第7小的数字是4
我们每次都是取k/2的数进行比较,有时候可能会遇到数组长度小于k/2的时候
此时k/2等于3,而上边的数组长度是2,我们此时将剪头指向它的末尾就可以了,这样的话,由于2<3,所以就会导致上边的数组1,2都被排除,造成下边的情况,
由于2个元素被排除,所以此时k=5,又由于上边的数组已经空了,我们只需要返回下边的数组的第5个数字就可以
从上边我们可以看到,无论是第奇数个还是第偶数个数字,对我们的算法并没有影响,而且在算法进行中,k的值都有可能从奇数变为偶数,最终都会变成1或者由于一个数组空了,直接返回结果,采用递归的思路,防止数组长度小于k/2,所以每次比较min(k/2,len(数组))对应的数字,把小的那个对应的数组的数字排除,两个新数组进入递归,并且k要减去排出的数字的个数,递归的出口就是当k=1或者其中一个数字长度是0了
小trick:我们分别找第(m+n+1)/2个,和(m+n+2)/2个,然后求其平均值即可,这对奇偶数均适用,m+n为奇数的话,那么其实(m+n+1)/2和(m+n+2)/2的值相等,相当于两个相同的数字相加再除以2,还是其本身
代码实现
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size();
int m = nums2.size();
int l = (n + m + 1) / 2;
int r = (n + m + 2) / 2;
return (getkpath(nums1,0,n-1,nums2,0,m-1,l) + getkpath(nums1,0,n-1,nums2,0,m-1,r)) / 2.0;
}
int getkpath(vector<int>& nums1, int start1, int end1, vector<int>& nums2, int start2, int end2, int k)
{
int len1 = end1 - start1 + 1;
int len2 = end2 - start2 + 1;
if (len1 > len2)
return getkpath(nums2, start2, end2, nums1, start1, end1, k);//如果有空数组,一定是2
if (len1 == 0)
return nums2[start2 + k - 1];
if (k == 1)
return min(nums1[start1], nums2[start2]);
int i = start1 + min(len1, k / 2) - 1;
int j = start2 + min(len2, k / 2) - 1;
//注意start还需要加
if (nums1[i] > nums2[j])
{
return getkpath(nums1,start1,end1,nums2,j+1,end2,k-(j-start2+1));
}
else
{
return getkpath(nums1, i+1, end1, nums2, start2, end2, k - (i - start1+1));
}
}
};
解法四
之后分析
代码实现
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
if(nums1.size() == 0)
return MedofArray(nums2);
if(nums2.size() == 0)
return MedofArray(nums1);
vector<int> num3;
int size = (nums1.size()+nums2.size());
int mid = size/2;
int flag = !(size%2);
int i,m1,m2,cur;
double a,b;
for(i = m1 = m2 = 0;i < size;i++)
{
a = m1 < nums1.size()?nums1[m1]:INT_MAX;//过界处理
b = m2 < nums2.size()?nums2[m2]:INT_MAX;//过界处理
//cout<<i<<" a "<<a<<" b "<<b<<endl;
if(a < b)
{
num3.push_back(nums1[m1]);
m1++;
}
else
{
num3.push_back(nums2[m2]);
m2++;
}
if(i == mid)
break;
}
return (num3[mid]+num3[mid-flag])/2.0;
}
double MedofArray(vector<int>& nums)
{
int mid = nums.size()/2;
int flag = !(nums.size()%2);
return (nums[mid]+nums[mid-flag])/2.0;
}
};