给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 。
请找出这两个有序数组的中位数。要求算法的时间复杂度为 O(log (m+n))。
示例 1:
nums1 = [1, 3] nums2 = [2] 中位数是 2.0
示例 2:
nums1 = [1, 2] nums2 = [3, 4] 中位数是 (2 + 3)/2 = 2.5
public double FindMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.Length;
int n = nums2.Length;
if(m>n){
//保证m<=n
int[] temp = nums1; nums1 = nums2;nums2 = temp;
int t = m; m = n; n = t;
}
int iMin = 0;
int iMax = m;//只需要在小的一个数组中寻找i
int halfLen = (m + n + 1) / 2;//根据公式,用来计算j
while(iMin <= iMax)
{
int i = (iMax + iMin) / 2; //2分法
int j = halfLen - i; //计算出对应的j
if(i < iMax && nums2[j-1] > nums1[i])
{
iMin = i+1; //数组1的i位置的值小了,数组2的j-1应该小于=数组1的i
}else if(i > iMin && nums1[i-1] > nums2[j])
{
iMax = i-1; //数组1的i位置的值大了,数组1的i-1应该小于=数组2的j
}else
{
//这个位置很合适了,求左边的最大值,注意特殊情况
int maxLeft = 0;
if(i == 0){
maxLeft = nums2[j-1];
}else if(j == 0)
{
maxLeft = nums1[i-1];
}else{
maxLeft = Math.Max(nums1[i-1],nums2[j-1]);
}
//奇数的话,直接返回左边最大,前面计算i和j会保证左边元素多1
if((m+n)%2 == 1) return maxLeft;
//计算右边的最小
int minRight = 0;
if(i == m)
{
minRight = nums2[j];
}else if(j == n)
{
minRight = nums1[i];
}else
{
minRight = Math.Min(nums2[j],nums1[i]);
}
return (maxLeft + minRight)/2f;
}
}
return 0.0;
}在两个有序的数组中找中位数,中位数是什么,就是可以把一段有序数组分成两段,右边那一段比前面那一段都大。那么就可以想,将有序数组1用i分成两半,同样的将有序数组2用j分成两半,数组1的i和i的右边跟数组2的j和j的右边合并,使他们的个数和左边合并的相同或则少一个,同时合成的右边全都比左边的大,那么在相同个数的情况下,中位数就=(左边最大+右边最小)/2,在左边个数比较多的情况下,中位数就=左边最大。
所以寻找i和j的时候需要保证两个条件,1.左边比右边的个数相同或则多一个。2.数组2第j位大于数组1第i-1位,数组1第i位大于数组2第j-1位,因为i和j是在右边数组,所以一定得保证比它本身上一个元素大。
如果按照这样子区分后,左边最大不是数组1的i-1就是数组2的j-1,而右边最大不是数组1的i就是数组2的j。
时间复杂度是:O(log(min(n,m)))