题目描述:
给定两个大小分别为 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
提示:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106
进阶:你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
第一种:暴力合并两个有序数组(归并),再过程中判断中位数
思路:
开辟一个新的数组help,长度为两个数组的长度和。使用两个指针分别指向两个数组的开头,比较两个指针所指的元素,将比较小的元素放入到新的数组中。之后继续上述的操作,直到将所有的数都放入到help中。实现很简单。代码如下
代码
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int length = nums1.length + nums2.length;
double result = 0;
//分别进行奇数偶数处理
if(length % 2 != 0){//奇数
result = getNum(nums1,nums2,length/2);
}else{//偶数
result = getNum(nums1,nums2,length/2-1)/2 + getNum(nums1,nums2,length/2)/2;
}
return result;
}
public double getNum(int[] nums1, int[] nums2, int k){
int[] result = new int[nums1.length+nums2.length];
int i = 0, j = 0;
int cur = 0;
while(i < nums1.length && j <nums2.length && cur <= k){
if(nums1[i] < nums2[j]) result[cur++] = nums1[i++];
else result[cur++] = nums2[j++];
}
while(i < nums1.length && cur <=k) result[cur++] = nums1[i++];
while(j < nums2.length && cur <=k) result[cur++] = nums2[j++];
return result[cur-1];
}
}
归并的时间复杂度与空间复杂度都是线性复杂度O(m+n)
。并不符合你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?这种我们首先想到二分查找。
第二种:确定中间分割线的二分查找
思路:
因为我们的两个数组都是有序的,那么一定有一种可以将两个数组看作一个数组,但是又不用合并数组的方法。我们可以想象有一条线将两个数组的元素分割开来:
类似下面这种:将左右两部分割为元素个数大概相等的两部分:
比如上面的数组为arr1,分割线右边的第一个角标为x;
第二个数组为arr2,分割线右边的第一个角标为y
需要满足arr1[x-1]<=arr2[y]&&arr[y-1]<=arr[x]
如果两个数组的长度和为偶数时:中位数取决于分割线左右两边的四个数
如果两个数组长度和为奇数时:中位数取决于分割线左边的两个数。
我们的主要任务就是确认分割线的位置,这还可以简化为确定分割线在arr1中的位置:即x的值,因为只要x确定,那么y的值也就确定了,分割线需要满足arr1[x-1]<=arr2[y]&&arr[y-1]<=arr[x]
并且x+y = (arr1.length+arr2.length+1)/2(我们默认如果长度和为奇数的话,让左边的元素个数多一个)
代码
class Solution {
public double findMedianSortedArrays(int[] arr1, int[] arr2) {
if(arr1.length>arr2.length){
//交换,符合第一个是较短的
int swap [] = arr2;
arr2 = arr1;
arr1=swap;
}
int len1 = arr1.length;
int len2 = arr2.length;
//左边的元素个数
int leftTotalNum = (len1+len2+1)/2;
int left=0;//二分查找的左边界(一开始的时候)
int right = len1;//二分查找的右边界(一开始的时候)
//二分查找
while(left<right){
int i= left +(right - left +1)/2 ;//从中间位置开始
int j = leftTotalNum-i;//arr2的中位线角标位置
if(arr1[i-1]>arr2[j]){
//下一轮搜索
right = i-1;//收缩右边的空间继续查找
}else
{
left = i;//收缩左边的空间继续查找
}
}
int i = left;//此时left为arr1中位线的右边的第一个角标
int j = leftTotalNum -i;
int arr1LeftMax = (i==0)?Integer.MIN_VALUE:arr1[i-1];
int arr1RightMin = (i==len1)?Integer.MAX_VALUE:arr1[i];
int arr2LeftMax = (j==0)?Integer.MIN_VALUE:arr2[j-1];
int arr2RightMin = (j==len2)?Integer.MAX_VALUE:arr2[j];
if((len1+len2)%2 == 1){//总数为奇数,则返回左边最大的
return Math.max(arr1LeftMax,arr2LeftMax);
}else{//总数是偶数,则返回 (leftMax+rightMin)/2
return ((double) Math.max(arr1LeftMax,arr2LeftMax)+Math.min(arr1RightMin,arr2RightMin))/2;
}
}
}