给定两个大小为 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
思路与代码
思路一
双指针(一个指向nums1 一个指向nums2) 按顺序遍历两个数组 找到处于中间位置的数字
时间复杂度 O(m + n)
来自leetcode中评论区代码
public double findMedianSortedArrays(int[] A, int[] B) {
int m = A.length;
int n = B.length;
int len = m + n;
int left = -1, right = -1;
int aStart = 0, bStart = 0;
for (int i = 0; i <= len / 2; i++) {
left = right;
if (aStart < m && (bStart >= n || A[aStart] < B[bStart])) {
right = A[aStart++];
} else {
right = B[bStart++];
}
}
if ((len & 1) == 0)
return (left + right) / 2.0;
else
return right;
}
思路二 进阶
时间复杂度 O(log(m+n))
看到log的时间复杂度,我们就知道肯定跟二分有关
1 要确定中位数,我们必须要找到中位的分界线num1Line
和 num2Line
,因为两个数组是分开的,所以每个数组都有各自的分界线。
2 那么如何找到分界线呢?
我们先确定左边一半的数字大小leftNum
,可用两个数组长度的一半来确定。
对其中一个nums1用二分法逼近其分界线,用leftNum - num1Line
来确定数组nums2的分界线num2Line
。
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if (nums1.length > nums2.length){
// 将nums1 设为较小长度的数组
int[] temp = nums1;
nums1 = nums2;
nums2 = temp;
}
int m = nums1.length;
int n = nums2.length;
int leftNum = (m + n) / 2; // 左边一半数字的个数
// 如果 m + n 是偶数 则左边应该有6个数 ,最终结果就是左边一半的最大值加上右边一半的最小值 再除以2
// 如果 m + n 是奇数 则左边应该有5个数, 最终结果就是右边一半的最小值
int L = 0, R = m; // L 与 R 来逼近nums1数组的左右一半的分界线
while (L < R) {
int i = (L + R + 1) / 2; // i的范围为[1,m] 代表nums1中属于左边一般的数字有i个
int j = leftNum - i; // 代表nums2中属于左边一半的数字有j个
if (nums1[i - 1] > nums2[j]) // 如果nums1左边数字的最大值还大于nums2右边数字的最小值
R--;
else L++; // 如果nums1左边数字的最大值不大于nums2右边数字的最小值
}
int num1Line = L; // 代表nums1中属于左边一般的数字有num1Line 个
int num2Line = leftNum - L; // 代表nums2中属于左边一般的数字有num2Line 个
if ((m + n) % 2 == 0){
int left = Math.max(
num1Line == 0 ? Integer.MIN_VALUE : nums1[num1Line-1],
num2Line == 0 ? Integer.MIN_VALUE : nums2[num2Line-1]
);
int right = Math.min(
num1Line == m ? Integer.MAX_VALUE : nums1[num1Line],
num2Line == n ? Integer.MAX_VALUE : nums2[num2Line]
);
return (left + right + 0.0) / 2;
}else{
return Math.min(
num1Line == m ? Integer.MAX_VALUE : nums1[num1Line],
num2Line == n ? Integer.MAX_VALUE : nums2[num2Line]
);
}
}
}