leetcode - 4.寻找两个正序数组的中位数

算法的时间复杂度应该为 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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一叶祇秋

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

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

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

打赏作者

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

抵扣说明:

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

余额充值