[leetcode日记]4.寻找两个正序数组中的中位数

题目

给定两个大小为 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;
}

感谢大家的支持和点赞。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邵政道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值