「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战」
1、题目
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
示例 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
示例 3:
输入:nums1 = [0,0], nums2 = [0,0]
输出:0.00000
示例 4:
输入:nums1 = [], nums2 = [1]
输出:1.00000
示例 5:
输入:nums1 = [2], nums2 = []
输出:2.00000
提示:
nums1.length == mnums2.length == n0 <= m <= 10000 <= n <= 10001 <= m + n <= 2000-106 <= nums1[i], nums2[i] <= 106
进阶: 你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?
2、思路
(递归)
找出两个正序数组的中位数等价于找出两个正序数组中的第k小数。如果两个数组的大小分别为n和m ,那么第 k = (n + m)/2 小数就是我们要求的中位数。
如何寻找第k小的元素?
过程如下:
1、考虑一般情况,我们在 nums1和nums2数组中各取前k/2个元素
我们默认nums1数组比nums2数组的有效长度小 。nums1数组的有效长度从i开始,nums2数组的有效长度从j开始,其中[i,si - 1]是nums1数组的前k / 2个元素,[j, sj - 1]是nums2数组的前 k / 2个元素。
2、接下来我们去比较nums1[si - 1]和nums2[sj - 1]的大小。
- 如果
nums1[si - 1] > nums2[sj - 1],则说明nums1中取的元素过多,nums2中取的元素过少。因此nums2中的前k/2个元素一定都小于等于第k小数,即nums2[j,sj-1]中元素。我们可以舍去这部分元素,在剩下的区间内去找第k - k / 2小的元素,也就是说第k小一定在[i,n]与[sj,m]中。 - 如果
nums1[si - 1] <= nums2[sj - 1],同理可说明nums2中的前k/2个元素一定都小于等于第k小数,即nums1[i,si-1]中元素。我们可以舍去这部分元素,在剩下的区间内去找第k - k / 2小的元素,也就是说第k小一定在[si,n]与[j,m]中。
3、递归过程2,每次可将问题的规模减少一半,最后剩下的一个数就是我们要找的第k小数。
递归边界:
- 当
nums1数组为空时,我们直接返回nums2数组的第k小数。 - 当
k == 1时,且两个数组均不为空,我们返回两个数组首元素的最小值,即min(nums1[i], nums2[j])。
奇偶分析:
-
当两个数组元素个数的总和
total为偶数时,找到第total / 2小left和第total / 2 + 1小right,结果是(left + right / 2.0)。 -
当
total为奇数时,找到第total / 2 + 1小,即为结果。
时间复杂度分析: ,且每次递归 的规模都减少一半,因此时间复杂度是.
3、c++代码
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int tot = nums1.size() + nums2.size();
if (tot % 2 == 0) {
int left = find(nums1, 0, nums2, 0, tot / 2);
int right = find(nums1, 0, nums2, 0, tot / 2 + 1);
return (left + right) / 2.0;
} else {
return find(nums1, 0, nums2, 0, tot / 2 + 1);
}
}
int find(vector<int>& nums1, int i, vector<int>& nums2, int j, int k) {
if (nums1.size() - i > nums2.size() - j) return find(nums2, j, nums1, i, k);
if (k == 1) {
if (nums1.size() == i) return nums2[j];
else return min(nums1[i], nums2[j]);
}
if (nums1.size() == i) return nums2[j + k - 1];
int si = min((int)nums1.size(), i + k / 2), sj = j + k - k / 2;
if (nums1[si - 1] > nums2[sj - 1])
return find(nums1, i, nums2, sj, k - (sj - j));
else
return find(nums1, si, nums2, j, k - (si - i));
}
};
4、java代码
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int total = nums1.length + nums2.length;
if(total % 2 == 0)
{
int left = f(nums1,0,nums2,0,total / 2);
int right = f(nums1,0,nums2,0,total / 2 + 1);
return (left + right) / 2.0;
}
else return f(nums1,0,nums2,0,total / 2 + 1);
}
static int f(int[] nums1,int i,int[] nums2,int j,int k)
{
//默认第一个是小的
if(nums1.length - i > nums2.length - j) return f(nums2,j,nums1,i,k);
//当第一个数组已经用完
if(nums1.length == i) return nums2[j + k - 1];
//当取第1个元素
if(k == 1) return Math.min(nums1[i],nums2[j]);
int si = Math.min(nums1.length,i + k / 2),sj = j + k - k / 2;
if(nums1[si - 1] > nums2[sj - 1])
{
return f(nums1,i,nums2,sj,k - (sj - j));
}
else
{
return f(nums1,si,nums2,j,k - (si - i));
}
}
}
原题链接: 4. 寻找两个正序数组的中位数