题目:给定两个大小为 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
算法思路:
- 传统思路:用一个循环遍历两个数字,找到中间数字的位置,要注意区分 m+n 是奇数偶数的场景,用两个变量分别记录两个数字遍历的位置;算法比较简单,但算法复杂度为 O(m+n),不符合题目要求;
- 二分算法:根据题目算法复杂度的要求 O(log(m + n)),应该借助二分算法的思路才能满足要求,每次排除一半的数据。题目是求中位数,其实就是求第 k 小数的一种特殊情况,而求第 k 小数有一种算法。由于数列是有序的,我们可以一半儿一半儿的排除。假设我们要找第 k 小数,我们可以每次循环排除掉 k/2 个数:比较两个数组的第 k/2 个数字,如果 k 是奇数,向下取整;如果哪个小,就表明该数组的前 k/2 个数字都不是第 k 小数字,所以可以排除;依次类推,每次排除剩余的一半数据。注意 m+n 的和要考虑奇数和偶数,偶数时要找到中间的两个数字并取平均值;若是奇数,只需要找到一个中间数字即可。
算法代码:根据算法思路2,写出的算法具体代码如下:
public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
// 区分奇数偶数
int middleLeft = (m + n + 1) / 2;
if ((m + n) % 2 == 1) { // 奇数情况
return getMiddleData(nums1, 0, m - 1, nums2, 0, n - 1, middleLeft);;
}
// 偶数情况
int middleRight = (m + n + 2) /2;
return (getMiddleData(nums1, 0, m - 1, nums2, 0, n - 1, middleLeft)
+ getMiddleData(nums1, 0, m - 1, nums2, 0, n - 1, middleRight)) * 0.5;
}
public static int getMiddleData(int[] num1, int start1, int end1, int[] num2, int start2, int end2, int k) {
int len1 = end1 - start1 + 1;
int len2 = end2 - start2 + 1;
//让 len1 的长度小于 len2,这样就能保证如果有数组空了,一定是 len1
if (len1 > len2) {
return getMiddleData(num2, start2, end2, num1, start1, end1, k);
}
if (len1 <= 0) {
return num2[start2 + k - 1];
}
if (k == 1) {
return Math.min(num1[start1], num2[start2]);
}
int middleValue = k / 2; // 二分查找,每次排除一半数据
// 定位数组1的位置
int indexNum1 = len1 > middleValue ? (start1 + middleValue - 1) : end1;
// 定位数组2的位置
int indexNum2 = len2 > middleValue ? (start2 + middleValue - 1) : end2;
if (num1[indexNum1] > num2[indexNum2]) { // 排除数组2 start2到indexNum2的数据
k = k - (indexNum2 - start2 + 1); // 剩余的待定位数据数量
return getMiddleData(num1, start1, end1, num2, indexNum2 + 1, end2, k);
} else { // 排除数组1 start1到indexNum1的数据
k = k - (indexNum1 - start1 + 1); // 剩余的待定位数据数量
return getMiddleData(num1, indexNum1 + 1, end1, num2, start2, end2, k);
}
}
如果你有疑问或更好的算法思路,欢迎留言交流!!!