4. 寻找两个正序数组的中位数
原题链接
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
示例 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
提示:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106
题解
常规思路是在创建一个数组 然后排序 这样的作法是O(nlog n)的时间复杂度 但是这题的要求复杂度在log(m+n)中,于是我们可以转换思想
将题目所求可以变成一个寻找第K个数 因为题目给出的是正序数组
找中位数k=(n + m) / 2,等价于找第k小的元素,k = (n + m) / 2
1、当一共有偶数个数时,找到第total / 2小left和第total / 2 + 1小right,结果是(left + right / 2.0)
2、当一共有奇数个数时,找到第total / 2 + 1小,即为结果
首先默认第一个数组比第二个数组短 如果不是 那么调换顺序
nums1从i 开始 nums2从j开始
[i,si-1]表示nums1里面前k/2个元素
[j,sj]表示的nums2里面前k-k/2个元素
当nums1[si - 1] > nums2[sj - 1]时,则表示第k小一定在[i,n]与[sj,m]中
也就是舍弃了nums2的前一部分
当nums1[si - 1] <= nums2[sj - 1]时,则表示第k小一定在[si,n]与[j,m]中
就是舍弃了nums1的前一部分
考虑边界的情况:
第一个数组用完
数组就两个元素
时间复杂度分析:k=(m+n)/2k=(m+n)/2,且每次递归 kk 的规模都减少一半,因此时间复杂度是 O(log(m+n))O(log(m+n)).
代码案例:
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int total = nums1.length + nums2.length;
if(total%2 == 0){
int left = f(nums1,0,nums2,0,total/2);
int right = f(nums1,0,nums2,0,total/2 +1);
return (left+right)/2.0 ;//除以2的话会取整
}else return f(nums1,0,nums2,0,total/2+1);
}
static int f(int[] nums1 ,int i , int[] nums2,int j,int k){
//默认第一个数组的长度小
if(nums1.length -i> nums2.length-j) return f( nums2 , j, nums1 ,i , k);
//如果第一个数组用完
if(nums1.length == i ) return nums2[j+k-1];
if(k== 1 ) return Math.min(nums1[i],nums2[j]);
//只有一个长度的时候
//两个有效长度
int si = Math.min(nums1.length,i + k /2) ;
//si如果不取最小的话 就会出现出界的问题 因为si可能为0 本身就短
int sj = j + k - k /2 ;
if(nums1[si-1] < nums2 [sj-1]) return f(nums1,si,nums2,j,k - (si - i));
else return f(nums1,i,nums2,sj,k -(sj-j));
}