给定两个大小为 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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
详解:
从有序数组找出中位数,那什么是中位数呢?
我们在衡量一组数据的时候,一般都根据平均数衡量(所有数的和除以数的个数)
但是有些场景,比如考完试需要衡量一个班的水平。
假如此次考试A班11个人,其中10个考了100,99,98,97,96,95,94,93,92,91,还有一个1个考了个0,那平均数是多少呢?86.8分。
还有B班也是11个人,他们都是考了86分。
这个时候哪个班强一些呢?实际感官肯定是A班,只是因为有个老鼠屎,才拉低了平均数,这个时候中位数就可能更好的衡量了。
将一个班的分数排名之后,处于最中间的分数即为中位数,也就是A班中位数95,B班还是86。
好了不多说了,回归题目。
可以简单思考发现:
当个数n为奇数的时候,中位数就是第n/2 + 1位置的数
当个数n为偶数的时候,中位数就是第n/2和n/2 + 1位置的数的平均值
那这很明显的可以看出来,这个题目是叫求第n/2和第n/2+1的数了
那自然延伸到两个有序数组求第K大的数
int findKthSmallestNum(int[] nums1, int start1, int[] nums2, int start2, int k)
当start1大于nums1的长度,那第K大就是nums2里面的第K大
当start2大于nums2的长度,那第K大就是nums1里面的第K大
为了求第K大,可以判断nums1和nums2的前面第k/2大的数(下面会讲为什么),也就是x = nums1[start1 + k / 2]和y = nums2[start2 + k / 2]的大小.
x和y的较小者(假如为x)可以断定第k大的数一定不在x所在数组的前k/2个数中,因为这两数组分出的这两段数刚好K个,第K大的如果在这里面那也一定是x和y的较大者y。但是这个较大者y不一定是这个方法的结果,因为这个第K大的数还有可能在x所在数组的后面,只是能确定第k大的数一定不在x所在数组的前k/2个数中。
那么问题规模可以缩小到k的一半,直到k等于1(此时可以直接通过比较得出结果)
再详细的思路也不及代码片行,那直接贴代码:
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int L = nums1.length + nums2.length;
int middleIndex = (L + 1) >> 1;
int middle = findKthSmallestNum(nums1, 0, nums2, 0, middleIndex);
if((L & 1) == 1){
return middle;
}else{
return (middle + findKthSmallestNum(nums1, 0, nums2, 0, middleIndex + 1)) / 2.0;
}
}
public int findKthSmallestNum(int[] nums1, int start1, int[] nums2, int start2, int k){
if(start1>= nums1.length){
return nums2[start2 + k -1];
}
if(start2 >= nums2.length){
return nums1[start1 + k -1];
}
if(k == 1){
return getMin(nums1[start1], nums2[start2]);
}
int middle = k >> 1;
if(start1 + middle > nums1.length){
return findKthSmallestNum(nums1, start1, nums2, start2 + middle, k - middle);
}else if(start2 + middle > nums2.length){
return findKthSmallestNum(nums1, start1 + middle, nums2, start2, k - middle);
}else if(nums1[start1 - 1 + middle] < nums2[start2 - 1 + middle]){
return findKthSmallestNum(nums1, start1 + middle, nums2, start2, k - middle);
}else{
return findKthSmallestNum(nums1, start1, nums2, start2 + middle, k - middle);
}
}
public int getMin(int a, int b){
return a < b ? a : b;
}
最后贴效率: