给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
审题:
中位数:一组有序数据中居于中间位置的数,奇数个最中间数字,偶数个中间两个数字的平均数。
思考:
因为对效率有要求,使用二分搜索。
解题:
解法一:二分搜索。
因为求两个数组的中位数,通过对比两个数组的中位数,然后调整对比的位置,保证两个数组左右两边的数字个数相同,直到最后找到平衡点:B[j−1]≤A[i] 且 A[i−1]≤B[j],需要注意的是,有些数组可能只有一位,或者没有,还有数组长度奇数偶数的问题。
public double findMedianSortedArraysI(int[] A, int[] B) {
int m = A.length;
int n = B.length;
// 保证B数组是最长或者最长的之一。这样可以减少判断次数。
if (m > n) {
int[] temp = A;
A = B;
B = temp;
int tep = m;
n = m;
m = tep;
}
int iMin = 0;
int iMax = m;
int halfLen = (m + n + 1) / 2;
while (iMin <= iMax) {
// 求出数组A的中位数的下标位置
int i = (iMin + iMax) / 2;
// B数组的位置可以由A的i得出,因为i和j左边的和==右边的和(个数)。
int j = halfLen - i;
if (i < iMax && B[j - 1] > A[i]) {// 太小,A[i]这个位置的数字,比B中间的数字小,需要变大i的值,i值变j值也变。
iMin = i + 1;// A数组A[i]左边太小,排除,再剩下较大的那一段找,B相应也许相左移动。
} else if (i > iMax && A[i - 1] > B[j]) {// 太大,A最大的数字比B中间的数字大,j需要右移。
// iMin大了i就大了,j就小了
iMax = i - 1;
} else {// 平衡点。B[j−1]≤A[i] 且 A[i−1]≤B[j]:
int maxLeft = 0;
if (i == 0) {// 0或者1位
maxLeft = B[j - 1];
} else if (j == 0) {// 两个都是0或者1位。
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 0.0;
}
方法二:参考
public double findMedianSortedArraysII(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
int left = (m + n + 1) / 2;
int right = (m + n + 2) / 2;
return (findKth(nums1, 0, nums2, 0, left) + findKth(nums1, 0, nums2, 0, right)) / 2.0;
}
//i: nums1的起始位置 j: nums2的起始位置
public int findKth(int[] nums1, int i, int[] nums2, int j, int k){
if( i >= nums1.length) return nums2[j + k - 1];//nums1为空数组
if( j >= nums2.length) return nums1[i + k - 1];//nums2为空数组
if(k == 1){
return Math.min(nums1[i], nums2[j]);
}
int midVal1 = (i + k / 2 - 1 < nums1.length) ? nums1[i + k / 2 - 1] : Integer.MAX_VALUE;
int midVal2 = (j + k / 2 - 1 < nums2.length) ? nums2[j + k / 2 - 1] : Integer.MAX_VALUE;
if(midVal1 < midVal2){
return findKth(nums1, i + k / 2, nums2, j , k - k / 2);
}else{
return findKth(nums1, i, nums2, j + k / 2 , k - k / 2);
}
}