算法的时间复杂度应该为 O(log (m+n)) 。
链接:https://leetcode.cn/problems/median-of-two-sorted-arrays
解法一:暴力求解
将nums1与nums2两个数组合并为一个有序数组,再分奇偶去算出中位数,但其解法的时间复杂度必然超出。
解法二:二分查找
在看到时间复杂度为O(log(m+n))时,第一时间就会联想到二分查找。
找出中位数就是找出两个数组中第 K 小的数字,令total=nums1Size+nums2Size,K=(total+1)/2,若是total是奇数,则其中位数就是第K小的数字,若为偶数,则其中位数就是 (第K小的值+第K+1小的值)/2 ,我们将nums1和nums2各分成两个部分,例如:
- 若nums1_left_max(即A3)<=nums2_right_min(即B3)并且 nums2_left_max(即B2)<=nums1_right_min(即A4)那么其中位数就是(left_max+right_min)/2
- 若nums1_left_max(即A3)>nums2_right_min(即B3)时,nums2_left_max(即B2)<=nums1_right_min(即A4)一定成立,要将分割线移动
如图:
同理:当total为奇数时,若
nums1_left_max<=nums2_right_min&&nums2_left_max<=nums1_right_min 那么其中位数就是 MIN(nums1_left_min,nums2_left_min)
由此可知:当我们找到
nums1_left_max<=nums2_right_min&&nums2_left_max<=nums1_right_min的位置时这道题目就基本解决了。
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
//将较小的数组赋给nums1数组-----降低时间复杂度
if (nums1Size > nums2Size)
{
int tmp = nums1Size;
nums1Size = nums2Size;
nums2Size = tmp;
int* p = nums1;
nums1 = nums2;
nums2 = p;
}
int len = nums1Size + nums2Size;
//得出数组合并后一半的元素个数(偶数正好是一半,奇数带上中间的那个值)
int total = (len + 1) / 2;
//从较短的数组中进行二分查找,查找到合适的切割线
//该二分查找与寻找值的二分查找不同在于分割线没有下标,需要我们进行自定义分割线
//比如我们将元素下标当成是该元素之前的分割线的序号
//要注意分割线越界的问题,因为我们无论如何定义分割线都总会有一个分割线的序号不在数组的下标中
int left = 0, right = nums1Size;
while (left < right)
{
//num1数组的分割线(偶数正好是一半,奇数带上中间的那个值)
int mid1 = left + (right - left) / 2;//可以通过下标知道取的元素个数
//num2数组的分割线
int mid2 = total - mid1;
//下面的代码是为了调整分割线
if (nums1[mid1] < nums2[mid2 - 1])//即A3<B3
{
left = mid1 + 1;
}
else
{
right = mid1;
}
}
int i = left;
int j = total - i;
//若分割线在nums1数组的最左边(即分割线左边无数字)令nums1最左边为无穷小
int nums1Left = (i == 0 ? INT_MIN : nums1[i - 1]);
//若分割线在nums1数组的最右边(即分割线右边无数字)令nums1最右边为无穷大
int nums1Right = (i == nums1Size ? INT_MAX : nums1[i]);
//若分割线在nums2数组的最左边(即分割线左边无数字)令nums2最左边为无穷小
int nums2Left = (j == 0 ? INT_MIN : nums2[j - 1]);
//若分割线在nums2数组的最右边(即分割线右边无数字)令nums2最右边为无穷大
int nums2Righ = (j == nums2Size ? INT_MAX : nums2[j]);
if ((nums1Size + nums2Size) % 2 == 1)
{
return nums1Left > nums2Left ? nums1Left : nums2Left;
}
else
{
double x = ((nums1Left > nums2Left ? nums1Left : nums2Left) + (nums1Right < nums2Righ ? nums1Right : nums2Righ)) / 2.0;
return x;
}
return 0;
}