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 == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106
进阶: 你能设计一个时间复杂度为 O(log (m+n))
的算法解决此问题吗?
2、思路
(递归) O ( l o g ( n + m ) ) O(log(n+m)) O(log(n+m))
找出两个正序数组的中位数等价于找出两个正序数组中的第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
小,即为结果。
时间复杂度分析: k = ( m + n ) / 2 k=(m+n)/2 k=(m+n)/2,且每次递归 k k k 的规模都减少一半,因此时间复杂度是 O ( l o g ( m + n ) ) O(log(m+n)) O(log(m+n)).
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. 寻找两个正序数组的中位数