给定两个大小为 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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
关键点
- 复杂度为O(log(m + n))
- 有序数组
思路
- 将两个数组拼成一个数组,再求中位数。
从复杂度上分析,如果是一个长度为m + n
的有序数组,则查找中位数的复杂度为O(1)
,直接取得length / 2
处即为答案。而将两个有序数组拼成一个有序数组的复杂度为O(m + n)
,已经超过了题目所要求的复杂度。不行
- 从log处思考,log级别的算法可以考虑二分法。
两个数组如何进行二分?
分析最终的答案与两个数组的中位数的关系:假设K = (n + m) / 2
,即为中位数的序号,找到两个数组的第K大的数即找到答案。
我们给A和B数组每人一个K / 2
个数字的机会。
Ka
A:----------|---------------
1 2
Kb
B:----------|-------------------------------
3 4
假设上面是A与B两个有序数组(从小到大),竖线处为Ka = A[K / 2]
与Kb = B[K / 2]
。
这四段的长度分别为L1, L2, L3, L4
比较Ka与Kb,若Ka > Kb
那么K一定不会落在小于Kb的这一段,也就是L3上(自己体会)。
那么也就是在L1 + L2 + L4
中查找第K - L3
个数。
每次都可以排除K / 2
段,K = (n + m) / 2
,所以总体复杂度为O(log(m + n))
代码
double min(double a,double b){
if(a>b)return b;
return a;
}
double calKth(int a[],int n,int b[],int m,int k){
if(n==0)
return b[k-1];
else if(m==0)
return a[k-1];
if(k==1)
return min(a[0],b[0]);
int ma=min(k/2,n),mb=min(k/2,m);
if(a[ma-1]>b[mb-1]){
return calKth(a,n,b+mb,m-mb,k-mb);
}
return calKth(a+ma,n-ma,b,m,k-ma);
}
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
if((nums1Size+nums2Size)%2){
return calKth(nums1,nums1Size,nums2,nums2Size,(nums1Size+nums2Size+1)/2);
}
int k1=(nums1Size+nums2Size)/2,k2=k1+1;
return (calKth(nums1,nums1Size,nums2,nums2Size,k1)+calKth(nums1,nums1Size,nums2,nums2Size,k2))/2.0;
}