题目
给定两个大小为 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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
分析
主要是有3种方法。
第一种方法是合并两个数组以后再返回中位数,这样的操作需要申请一个新的空间把两个有序数组放进去,需要的开销是O(n+m)。原则上是不满足题意的。思路比较简单就不详细展开了。对应下面的代码2
第二种方法是根据中位数的定义,当 m+n是奇数时,中位数是两个有序数组中的第 (m+n)/2 个元素,当 m+n是偶数时,中位数是两个有序数组中的第 (m+n)/2个元素和第 (m+n)/2+1个元素的平均值。因此,这道题可以转化成寻找两个有序数组中的第 k 小的数,其中 k 为 (m+n)/2 或 (m+n)/2+1。这种复杂度是lg(m+n)量级。
第三种方法理论上的时间复杂度是最优的,是lg(min(m,n))量级。思路是只在第一个数组里进行二分查找,找到第一个分割线,然后根据中位数的性质找到另外一个数组里的分割线。最终的中位数将由这个分割线左右的数字产生。详细对应代码一,我写了相对详细的注释,如果还有问题可以在评论区留言问我。
代码1
//时间复杂度lg(min(m,n))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)>(b)?(b):(a))
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
if (nums1Size > nums2Size) { //保证数组1长度小于数组2长度,算法的时间复杂度是lg(nums1Size),所以希望nums1Size为较小的那一个
return findMedianSortedArrays(nums2,nums2Size, nums1,nums1Size);
}
int m = nums1Size;
int n = nums2Size;
int left = 0, right = m;
// median1:前一部分的最大值
// median2:后一部分的最小值
int median1 = 0, median2 = 0;
while (left <= right) {
// 前一部分包含 nums1[0 .. i-1] 和 nums2[0 .. j-1]
// 后一部分包含 nums1[i .. m-1] 和 nums2[j .. n-1]
int i = (left + right) / 2;
int j = (m + n + 1) / 2 - i; //j是由i的位置确定的,i,j作为两条分割线,两条分割线左右一共4个数
// nums_im1, nums_i, nums_jm1, nums_j 分别表示 nums1[i-1], nums1[i], nums2[j-1], nums2[j]
// 最终收敛时,中位数结果一定可以由这四个数产生
int nums_im1 = (i == 0 ? INT_MIN : nums1[i - 1]); //数组1分割线左侧最大值
int nums_i = (i == m ? INT_MAX : nums1[i]); //数组1分割线右侧最小值
int nums_jm1 = (j == 0 ? INT_MIN : nums2[j - 1]); //数组2分割线左侧最大值
int nums_j = (j == n ? INT_MAX : nums2[j]); //数组2分割线右侧最小值
if (nums_im1 <= nums_j) { //若数组1分割线左侧的数比数组2分割线右侧的数要小,就更新left
// 暂存一下现在的分割线附近可能产生中位数的数(跳出循环、返回结果时会用到)
median1 = max(nums_im1, nums_jm1);
median2 = min(nums_i, nums_j);
left = i + 1;
} else { //否则更新right
right = i - 1;
}
}
//已经收敛,根据总量的奇偶返回平均中位数或中位数
return (m + n) % 2 == 0 ? (median1 + median2) / 2.0 : median1;
}
代码2
//时间复杂度为O(m+n)
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
int len = nums1Size + nums2Size;
int mid = len/2;
int *nums = (int *)malloc(sizeof(int)*len);
int n1 = 0;
int n2 = 0;
int n = 0;
int leftsize;
while(n1<nums1Size&&n2<nums2Size) { //合并数组
if(nums1[n1]>=nums2[n2]) {
nums[n++]= nums2[n2++];
} else {
nums[n++]= nums1[n1++];
}
}
if(n1<nums1Size) { //如果是先把nums2拷贝完,剩下部分由nums1填充,反之由nums2填充
memcpy(nums+n,nums1+n1,(nums1Size-n1)*sizeof(int));
} else {
memcpy(nums+n,nums2+n2,(nums2Size-n2)*sizeof(int));
}
return (len%2==0)?(nums[mid]+nums[mid-1])/2.0:nums[mid]*1.0;
}
感谢大家的支持和点赞。