寻找两个正序数组的中位数
难度困难3335
给定两个大小为 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
解法一:
思路:
- 两个数组合并为一个有序数组
- 通过判断奇数还是偶数进行返回中位数
代码
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int[] nums; //分配一个新的数组,合并后的数组
int m = nums1.length;//长度
int n = nums2.length;
nums = new int[m + n];//新的数组的长度
/**
* 排除特殊的数据
*/
if (m == 0) {
if (n % 2 == 0) {
return (nums2[n / 2 - 1] + nums2[n / 2]) / 2.0;
} else {
return nums2[n / 2];
}
}
if (n == 0) {
if (m % 2 == 0) {
return (nums1[m / 2 - 1] + nums1[m / 2]) / 2.0;
} else {
return nums1[m / 2];
}
}
int count = 0; //新数组的,下标
int i = 0, j = 0;
while (count != (m + n)) { //当count=(n+m)时则表明,所有数据已经放入
if (i == m) { //i表示数组一,当i=M时,则表明第一个数组已经全部放入子女数组
while (j != n) {//后续继续放入第二个数组即可
nums[count++] = nums2[j++];
}
break;
}
if (j == n) {//同上
while (i != m) {
nums[count++] = nums1[i++];
}
break;
}
if (nums1[i] < nums2[j]) { //排序有小到大
nums[count++] = nums1[i++];
} else {
nums[count++] = nums2[j++];
}
}
if (count % 2 == 0) { //判断奇偶数
return (nums[count / 2 - 1] + nums[count / 2]) / 2.0;
} else {
return nums[count / 2];
}
}
解法二:
不需要将两个数组合并,只需要找的中间位置就行.
思路
- 用 len 表示合并后数组的长度,如果是奇数,我们需要知道第 (len+1)/2 个数就可以了,如果遍历的话需要遍历 int(len/2 ) + 1 次。如果是偶数,我们需要知道第 len/2和 len/2+1 个数,也是需要遍历 len/2+1 次。所以遍历的话,奇数和偶数都是 len/2+1 次。
- 返回中位数的话,奇数需要最后一次遍历的结果就可以了,偶数需要最后一次和上一次遍历的结果。所以我们用两个变量 left 和 right,right 保存当前循环的结果,在每次循环前将 right 的值赋给 left。这样在最后一次循环的时候,left 将得到 right 的值,也就是上一次循环的结果,接下来 right 更新为最后一次的结果。
- 循环中该怎么写,什么时候 A 数组后移,什么时候 B 数组后移。用 aStart 和 bStart 分别表示当前指向 A 数组和 B 数组的位置。如果 aStart 还没有到最后并且此时 A 位置的数字小于 B 位置的数组,那么就可以后移了。也就是aStart<m&&A[aStart]< B[bStart]。
- 但如果 B 数组此刻已经没有数字了,继续取数字 B[ bStart ],则会越界,所以判断下 bStart 是否大于数组长度了,这样 || 后边的就不会执行了,也就不会导致错误了,所以为 aStart<m&&((bStart) >= n||A[aStart]<B[bStart]) 。
代码:
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;//表示在当前数组AB的位置
for (int i = 0; i <= len / 2; i++) {
left = right;//**每次循环前将 right 的值赋给 left
//如果 B 数组此刻已经没有数字了,继续取数字 B[ bStart ],
// 则会越界,所以判断下 bStart 是否大于数组长度了,
// 这样 || 后边的就不会执行了,也就不会导致错误了,
// 所以(aStart < m && (bStart >= n || A[aStart] < B[bStart])) 。
if (aStart < m && (bStart >= n || A[aStart] < B[bStart])) {
right = A[aStart++];
} else {
right = B[bStart++];
}
}
if ((len % 2) == 0)
return (left + right) / 2.0;
else
return right;
}