题目
给定两个大小分别为 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
示例 3:
输入:nums1 = [0,0], nums2 = [0,0]
输出:0.00000
示例 4:
输入:nums1 = [], nums2 = [1]
输出:1.00000
示例 5:
输入:nums1 = [2], nums2 = []
输出:2.00000
来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/me…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
- 基本思路
用一条边界把两个数组分为左右两个相等的部分,若为奇数个,则让左边比右边多一个,然后通过边界值去求中位数。
偶数情况
中位数 = (左边最大值 + 右边最小值)/ 2
奇数情况
中位数 = 左边最大值
- 特殊情况处理
i或者j左边没有值,则左边最大值为j-1或者i-1的位置上的值
此时左边最大值为:
maxLeft = B[j-1]
此时左边最大值为:
maxLeft = A[i-1]
i或者j右边没有值,其处理方式与上述类似
i和j的关系分析
- 对于偶数情况
i+j = (m+n)/2
- 对于奇数情况
i+j = (m+n+1)/2
- 合并
因为计算机处理
int类型除法,偶数情况给分子加1并不会影响其结果,因此奇数偶数情况都可以用下面的关系表示:
i+j = (m+n+1)/2
因此:j = (m+n+1)/2 - 1
- 问题转化 因为只要知道了i就能确定j的位置,因此问题转换为只要找到准确的i即可。因为找到i就可以计算j,有了i和j就可以求得左边最大值和右边最小值,然后计算中位数。
对于在一个数组中,找到一个i的位置,可以使用二分法来进行查找,因此该算法的时间复杂度是O(log(min(m, n)))的。
- 整体流程
算法
public double findMedianSortedArrays(int[] A, int[] B) {
// 第一步:保证第一个数组的长度要小于第二个数组,可以利用递归来实现
int m = A.length;
int n = B.length;
if (m > n) {
return findMedianSortedArrays(B, A);
}
// 第二步:初始化i的区间
int iMin = 0, iMax = m;
while (iMin <= iMax) {
// 对于i使用二分法,取区间中间值
int i = (iMin + iMax) / 2;
int j = (m + n + 1) / 2 - i;
// 交叉判断当前的i位置是否是正确的位置
if (j != 0 && i != m && B[j-1] > A[i]) {
// i需要增大
iMin = i + 1;
} else if (i != 0 && j != n && A[i-1] > B[j]) {
iMax = i - 1;
} else {
// 找到了合适的位置,先找左边最大值
int maxLeft = 0;
if (i == 0) {
maxLeft = B[j - 1];
} else if (j == 0) {
maxLeft = A[i - 1];
} else {
maxLeft = Math.max(A[i - 1], B[j - 1]);
}
// 判断总数是奇数还是偶数,如果是奇数,则直接返回左边最大值
if ((m + n) % 2 == 1) {
return maxLeft;
}
// 如果是偶数,还需要求右边最小值,然后求平均
int minRight = 0;
if (i == m) {
minRight = B[j];
} else if (j == n) {
minRight = A[i];
} else {
minRight = Math.min(B[j], A[i]);
}
return (maxLeft + minRight) / 2.0;
}
}
return -1.0;
}