写在前面:
题目链接:LeetCode4. 寻找两个正序数组的中位数
编程语言:C++
题目难度:困难
一、题目描述
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
提示:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106
二、题目分析&解题思路&代码实现
2.1 归并法
看到这样的描述,大家应该很快想到了归并排序的原理,如果这里对归并排序不太了解的可以参考以下博客:
十大排序算法思路&代码实现(持续更新中)
或者也可以直接看下面的解题思路:
两个有序数组
1 , 3
2 , 4
我们只需要新建一个 vector ,然后分别从两个数组,从头到尾数组元素挨个进行比较小的就插入
vector<int> vctResult;
int i = 0;
int j = 0;
while(i< nums1.size() && j < nums2.size())
{
while((i < nums1.size()) && (j < nums2.size()) && nums1[i] <= nums2[j])//这里一定要注意数组越界问题
{
//小的插入
vctResult.push_back(nums1[i]);
i++;
}
while((i < nums1.size()) && (j < nums2.size()) && nums2[j] <= nums1[i])
{
vctResult.push_back(nums2[j]);
j++;
}
}
//如果两个数组size 不相等,那么剩下的肯定就是大的数,直接插入即可
while(i < nums1.size())
{
vctResult.push_back(nums1[i]);
i++;
}
while(j < nums2.size())
{
vctResult.push_back(nums2[j]);
j++;
}
最后构建出一个新的数组:
1 , 2 , 3 ,4
接着我们只需要判断数组的 size 是基数还是偶数即可:
int mid = (nums1.size() + nums2.size())/2;
if(vctResult.size()%2==0)
{
//偶数取中间两个 除以 2.0(注意返回值为 double)
dResult = (vctResult[mid] + vctResult[mid-1])/2.0;
}
else
{
//奇数取中间一位即可
dResult = vctResult[mid];
}
2.1.1 完整代码示例:
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
double dResult = 0.0;
int mid = 0;
mid = (nums1.size() + nums2.size())/2;
vector<int> vctResult;//合并后的新数组
int i = 0;
int j = 0;
while(i< nums1.size() && j < nums2.size())
{
while((i < nums1.size()) && (j < nums2.size()) && nums1[i] <= nums2[j])//这里一定要注意数组越界问题
{
//小的插入
vctResult.push_back(nums1[i]);
i++;
}
while((i < nums1.size()) && (j < nums2.size()) && nums2[j] <= nums1[i])
{
vctResult.push_back(nums2[j]);
j++;
}
}
//如果两个数组size 不相等,那么剩下的肯定就是大的数,直接插入即可
while(i < nums1.size())
{
vctResult.push_back(nums1[i]);
i++;
}
while(j < nums2.size())
{
vctResult.push_back(nums2[j]);
j++;
}
if(vctResult.size()%2==0)
{
//偶数取中间两个 除以 2.0(注意返回值为 double)
dResult = (vctResult[mid] + vctResult[mid-1])/2.0;
}
else
{
//奇数取中间一位即可
dResult = vctResult[mid];
}
return dResult;
}
};
2.1.2 运行结果:
这里也是通过了,复杂度也是O(m+n) ,但同时也开辟了O(m+n)的空间,空间复杂度较高;
2.1.3 归并优化
这一步还可以再做优化,因为我们只需要合并到 新的数组size > mid 即可,后面的也不需要遍历,也不要再合并了,因此可以,将时间复杂度和空间复杂度都降低一半为:O(1/2(m+n))
代码示例:
class Solution {
public:
bool isMidFind(vector<int>& vctResult, int& mid, double&dResult,int& size)
{
if(vctResult.size() > mid)
{
if(size %2 == 0)//偶数取中间两位
{
dResult = (vctResult[mid] + vctResult[mid-1])/2.0;
}
else//奇数直接取即可
{
dResult = vctResult[mid];
}
return true;
}
else
{
return false;
}
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
double dResult = 0.0;
int mid = 0;
mid = (nums1.size() + nums2.size())/2;
int size = nums1.size() + nums2.size();
vector<int> vctResult;//合并后的新数组
int i = 0;
int j = 0;
while(i< nums1.size() && j < nums2.size())
{
while((i < nums1.size()) && (j < nums2.size()) && nums1[i] <= nums2[j])//这里一定要注意数组越界问题
{
//小的数插入
vctResult.push_back(nums1[i]);
if(isMidFind(vctResult, mid,dResult,size))
{
//找到了直接return 即可
return dResult;
}
i++;
}
while((i < nums1.size()) && (j < nums2.size()) && nums2[j] <= nums1[i])
{
vctResult.push_back(nums2[j]);
if(isMidFind(vctResult, mid,dResult,size))
{
return dResult;
}
j++;
}
}
//如果两个数组size 不相等,那么剩下的肯定就是大的数,直接插入即可
while(i < nums1.size())
{
vctResult.push_back(nums1[i]);
if(isMidFind(vctResult, mid,dResult,size))
{
return dResult;
}
i++;
}
while(j < nums2.size())
{
vctResult.push_back(nums2[j]);
if(isMidFind(vctResult, mid,dResult,size))
{
return dResult;
}
j++;
}
return dResult;
}
};
2.1.3.1 运行结果
可以看到时间和空间复杂度都有所降低。