LeetCode 4.寻找两个正序数组的中位数题目链接:

​https://leetcode-cn.com/problems/median-of-two-sorted-arrays/​


题目描述:

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。


示例 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


示例 5:

输入:nums1 = [2], nums2 = []

输出:2.00000


提示:

1. nums1.length == m

2. nums2.length == n

3. 0 <= m <= 1000

4. 0 <= n <= 1000

5. 1 <= m + n <= 2000

6. -106 <= nums1[i], nums2[i] <= 106


进阶:

你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?


题目分析:

这道题可以直接采用合并数组求数组中位数的解法,首先新建长度为两个数组元素和的合并后数组。判断两个数组是否为空,如果数组1为空,则直接返回数组2的中位数;如果数组2为空,则直接返回数组1的中位数。当两个数组同时不为空的情况下,我们建立两个指针,同时遍历两个数组,把两个指针指向的值小的元素放入合并数组。最后判断数组长度奇偶性,进行相应计算返回即可。(见题解一)

当然,我们也可以优化算法,采用二分查找法,分别对两个数组进行分割。首先我们要避免数组下标越界,把数组1设定为短数组,数组2设定为长数组,并分别获取数组长度。定义变量存放左边的所有元素需要满足的个数,然后在 num1 的区间里面查找恰当的分割线,对区间进行搜索。最后通过判断两个数组元素和的奇偶性,返回数组中位数。分析过程较为复杂,详见LeetCode官方题解(​https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-s-114/​)。


题解一(合并数组法):

执行用时: 3 ms

内存消耗: 39.4 MB

class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
// 定义合并后数组长度
int length = nums1.length + nums2.length;
// 定义合并后的数组
int[] array = new int[length];

// 如果数组1为空,则直接拷贝数组2元素到新建数组
if (nums1.length == 0)
System.arraycopy(nums2, 0, array, 0, nums2.length);
// 如果数组2为空,则直接拷贝数组1元素到新建数组
if (nums2.length == 0)
System.arraycopy(nums1, 0, array, 0, nums1.length);


// 如果数组1和数组2都不为空
if (nums1.length != 0 && nums2.length != 0) {
// 定义数组1和数组2指针
int i = 0, j = 0;
// 同时遍历数组1和数组2
for (int index = 0; index < length; ++index) {
// 如果数组1和数组2指针都未出界
if (i < nums1.length && j < nums2.length) {
// 当数组1当前指向的元素小于等于数组2当前指向的元素时
if (nums1[i] <= nums2[j])
// 拷贝数组1当前元素到新数组
array[index] = nums1[i++];
// 否则拷贝数组2当前元素到新数组
else
array[index] = nums2[j++];
}
// 如果数组1已经拷贝完,数组2剩下元素直接追加到新数组后面
else if (i == nums1.length && j < nums2.length) {
array[index] = nums2[j++];
}
// 如果数组2已经拷贝完,数组1剩下元素直接追加到新数组后面
else if (i < nums1.length && j == nums2.length) {
array[index] = nums1[i++];
}
}
}

// 获取数组中点位置
int mid = length / 2;
// 如果新数组长度为偶数
if (length % 2 == 0)
// 返回新数组中间两个元素平均值
return (double) (array[mid - 1] + array[mid]) / 2;
// 如果新数组长度为奇数
else
// 返回数组中间的元素
return array[mid];
}
}


题解二(二分查找法):

执行用时: 3 ms

内存消耗: 39.8 MB

class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
// 避免数组下标越界,设定数组1为短数组,数组2为长数组
if (nums1.length > nums2.length) {
int[] temp = nums1;
nums1 = nums2;
nums2 = temp;
}


// 获取每个数组的长度
int m = nums1.length;
int n = nums2.length;


// 定义变量存放左边的所有元素需要满足的个数
int totalLeft = (m + n + 1) / 2;


// 在 num1 的区间 [0, m] 里面查找恰当的分割线
// 使得 nums1[i - 1] <= nums2[j] && nums2[j - 1] <= nums1[i]
int left = 0;
int right = m;


// 当左边界小于右边界执行循环
while (left < right) {
// 取区间中点
int i = left + (right - left + 1) / 2;
int j = totalLeft - i;
if (nums1[i - 1] > nums2[j])
// 下一轮搜索的区间 [left, i - 1]
right = i - 1;
else
// 下一轮搜索的区间 [i, right]
left = i;
}


int i = left;
int j = totalLeft - i;


int nums1LeftMax = i == 0 ? Integer.MIN_VALUE : nums1[i - 1];
int nums1RightMin = i == m ? Integer.MAX_VALUE : nums1[i];
int nums2LeftMax = j == 0 ? Integer.MIN_VALUE : nums2[j - 1];
int nums2RightMin = j == n ? Integer.MAX_VALUE : nums2[j];


// 如果两个数组元素个数和为奇数,取 数组1左子区间 与 数组2左子区间 的最大值即为中位数
if (((m + n) % 2) == 1)
return Math.max(nums1LeftMax, nums2LeftMax);
// 否则取 (数组1左子区间 与 数组2左子区间 的最大值) 与 (数组1右子区间 与 数组2右子区间 的最小值) 取平均即为中位数
else
return (double) ((Math.max(nums1LeftMax, nums2LeftMax) + Math.min(nums1RightMin, nums2RightMin))) / 2;
}
}


题目来源:力扣(LeetCode)