题目描述
// 4. 寻找两个正序数组的中位数
// 给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。
// 请你找出并返回这两个正序数组的 中位数 。
// 要求时间复杂度为 O(log(m + n))
题解
数组归并法
///////////////////////////// 数组归并 ///////////////////////////
// 力扣
// 非最优解
// 参考归并排序的merge函数:https://blog.csdn.net/fisherish/article/details/113835444
// 执行用时:3 ms, 在所有 Java 提交中击败了81.99%的用户
// 内存消耗:39.5 MB, 在所有 Java 提交中击败了86.00%的用户
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int len1 = nums1.length;
int len2 = nums2.length;
int i = 0, j = 0;
int[] res = new int[len1+len2];
int t = 0;
double result = 0d;
while (i < len1 && j < len2) {
if (nums1[i] <= nums2[j])
res[t++] = nums1[i++];
else {
res[t++] = nums2[j++];
}
}
while (i < len1)
res[t++] = nums1[i++];
while (j < len2)
res[t++] = nums2[j++];
if ((len1 + len2) % 2 == 0)
result = (res[(len1 + len2) / 2 - 1] + res[(len1 + len2) / 2]) / 2d;
else
result = res[(len1 + len2) / 2] * 1d;
return result;
}
}
二分法(最优解)
//////////////////////////// 二分查找 /////////////////////////////
// 最优解
//
// 获取nums1的长度len1,nums2的长度len2,
// 第一个if是两个数组交换,如果nums1比nums2长,把nums1和nums2互换,
// 这样nums1短nums2长。
//
// nums1 [2, 3, 4, 5] len1
// nums2 [4, 5, 7] len2
// min i max
// nums1 [4, 5, 7] len1
// nums2 [2, 3, 4, 5] len2
// mid=4 j
// 将min初始化为0,max初始化为nums1的长度,
// mid为固定常数,为两数组在合并长度下的中点索引(或中间位靠左的索引),
// while循环,终止条件为min和max指针相遇,
// 定义二分法在两个数组的中点切分点i和j,i和j分别将nums1和nums2数组二分为:
// left_part | right_part
// nums1[0], nums1[1], ..., nums1[i-1] | nums1[i], nums1[i+1], ..., nums1[len1-1]
// nums2[0], nums2[1], ..., nums2[j-1] | nums2[j], nums2[j+1], ..., nums2[len2-1]
// 如果两个数组总长度为偶数,这其实左半部分(包括num1和nums2)的长度就等于
// 右半部分长度,如果两个数组总长度为奇数,左半部分长度等于右半部分长度+1。
// 我们需要保证左半部分的元素小于等于右半部分的元素。
// 又由于同数组里的元素本来就升序,同数组内本来就是左边小右边大,那我们只需要维护
// 左右半边中,上下不同数组之间的元素大小关系即可。
//
// 第一个if else段落,在保证i不越界前提下,如果左半边nums2中最大数nums2[j - 1],
// 比右半边nums1中最小数nums1[i]还要大,nums1[i]不符合条件,将左指针min右移到i + 1。
// 在i不越界的前提下,如果左半边nums1中最大数nums1[i - 1]比右半边
// nums2中最小数nums2[j]还要大,nums1[i - 1]不符合条件,右指针max左移到i - 1。
// 如此反复,直到左半边都小于等于右半边的元素,执行else。
//
// 第二层if else 段落:定义maxLeft用于存储左半边最大值。
// 这时如果中点切分点i为0,即左半边在nums1中无元素。令nums2[j - 1]赋给maxLeft。
// 如果中点切分点j为0,即左半边在nums2中无元素,令nums1[i - 1]赋给maxLeft。
// 如果nums1和nums2在左半边都有值,两数组在左半边的最大值里取最大,赋给maxLeft。
// 即maxLeft = Math.max(nums1[i - 1], nums2[j - 1])。
//
// 如果两个数组长度为奇数,中位数就是maxLeft,直接返回maxLeft
// 如果两个数组长度为偶数,按照同样的逻辑取右半边最小值minRight,
// 最后返回左半边最大值maxLeft和右半边最小值minRight的均值,
// (maxLeft + minRight) / 2.0 就是中位数。
// 执行用时:2 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:39.6 MB, 在所有 Java 提交中击败了83.45%的用户
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int len1 = nums1.length;
int len2 = nums2.length;
if (len1 > len2) {
int[] temp = nums1; nums1 = nums2; nums2 = temp;
int tmp = len1; len1 = len2; len2 = tmp;
}
int min = 0, max = len1, mid = (len1 + len2 + 1) / 2;
while (min <= max) {
int i = (min + max) / 2;
int j = mid - i;
if (i < max && nums2[j - 1] > nums1[i]) {
min = i + 1;
}
else if (i > min && nums1[i - 1] > nums2[j]) {
max = i - 1;
}
else {
int maxLeft = 0;
if (i == 0) maxLeft = nums2[j - 1];
else if (j == 0) maxLeft = nums1[i - 1];
else maxLeft = Math.max(nums1[i - 1], nums2[j - 1]);
if ((len1 + len2) % 2 == 1) return maxLeft;
int minRight = 0;
if (i == len1) minRight = nums2[j];
else if (j == len2) minRight = nums1[i];
else minRight = Math.min(nums2[j], nums1[i]);
return (maxLeft + minRight) / 2.0;
}
}
return 0;
}
}