题目描述
给定两个大小为 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
首先,求一个有序数组的中位数,可以取第(length+1)/2和第(length+2)/2位的平均数
其次,我们按照一个例子来整理一下解题的大致思路
假设我们要从两个数组中取出第4位的元素
第一步,从两个数组中分别取第4/2=2位
这样我们得到两个元素,分别是数组1中的3和数组2中的4,但是这样并不能保证我们取到的四个元素就是两个数组中的前4位(比如数组2是【4,6,8,10】),考虑一下这样到底能保证什么呢?其实它可以保证这样一件事情:数组1中的1和3,一定是最终结果中的两位,因为哪怕数组2中4之前的元素都比数组1中的元素要小,数组一中的1和3依然是有身份的~所以现在情况如下图所示
第二步,确定剩下的两位(剩下的K-K/2位,而不是简单的K/2,原因之后会说),而数组1这次可以直接从5开始找,这次是两个数组分别找各自的第1位
显然这次我们又确定了1位,此时情况如下
第三步:此时只剩一位未能确认,这就简单了,从两边分别去最小的,其中的较小的值就是剩下的那一位,而“剩一位未能确认”也是我们的递归边界,这样我们就已经明白整个过程的大体思路了,剩下的一些问题就比较容易理解了
关于之间提到的K-K/2而不是K/2的问题,主要是屏蔽奇偶性带来的一些问题,比如说我们在两个数组中取前5位
K值 | 5 | 5/2=2 | 2/2=1 |
每组取的前n位 | 5/2=2 | 2/2=1 | 边界 |
确定位数 | 2 | 2+1=2 | 2+1+1=4 |
可见,最终我们只得到了前4位,而如果用K-K/2
K值 | 5 | 5-5/2=3 | 3-3/2=2 | 2-2/2=1 |
每组取的前n位 | 5/2=2 | 3/2=1 | 2/2=1 | 边界 |
确定位数 | 2 | 2+1=2 | 2+1+1=4 | 2+1+1+1=5 |
这样我们得到的才是正确结果
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length,n = nums2.length;
int l = (m+n+1)/2;
int r = (m+n+2)/2;
return (getKth(nums1,0,nums2,0,l)+getKth(nums1,0,nums2,0,r))/2.0;
}
private double getKth(int[] nums1, int start1, int[] nums2, int start2, int k) {
//这里是当一个数组中的元素已经全部有身份了,但是还有空余席位,那就可以直接从另一个数组中取
if(start1>nums1.length-1) return nums2[start2 + k - 1];
if(start2>nums2.length-1) return nums1[start1 + k - 1];
if(k == 1) return Math.min(nums1[start1], nums2[start2]);
int temp1 = start1+k/2-1<nums1.length?nums1[start1+k/2-1]:Integer.MAX_VALUE;
int temp2 = start2+k/2-1<nums2.length?nums2[start2+k/2-1]:Integer.MAX_VALUE;
if(temp1<temp2)
//注意这个地方是K-k/2,而不是K/2,目的是屏蔽一些奇偶性的问题
return getKth(nums1, start1 + k / 2, nums2, start2, k-k/2);
else
return getKth(nums1,start1,nums2,start2+k/2,k-k/2);
}
}