leetcode数组练习题:两个正序数组的中位数

问题描述:

寻找两个正序数组的中位数
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。算法的时间复杂度应该为 O(log (m+n))


问题示例

示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

取值范围
 nums1.length == m 
 nums2.length == n 
 0 <= m <= 1000 
 0 <= n <= 1000 
 1 <= m + n <= 2000
-10^6^ <= nums1[i], nums2[i] <= 10^6^

解题思路及代码:

  1. 第一种思路就是先合并数组,然后再通过下标找到中位数,这种方法的时间复杂度为O(m+n)
  2. 第二种思路就是不合并数组,只去两数组中找到中位数所在的位置,找到后再取出即可,此时可以使用二分法来找到目标元素同时降低时间复杂度,时间复杂度为O(log(m+n))

第一种方法时间复杂度不满足题目要求,不过也可解题其代码如下:

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        vector<int> nums3;
        for(const auto &e:nums1)
        {
            nums3.push_back(e);
        }
        for(const auto &e:nums2)
        {
            nums3.push_back(e);
        }
        sort(nums3.begin(),nums3.end());
        if(nums3.size() % 2 != 0)
        {
            return (double)nums3[nums3.size() / 2];
        }
        else
        {
            double mid = ((double)nums3[nums3.size() / 2 - 1] + (double)nums3[nums3.size() / 2]) / 2;
            return mid;
        }
        return 0;
    }
};

第二种方法最终目标就是要找到中位数的位置,这其中涉及到m+n的奇偶性问题,情况如下:

  • 当m+n为奇数时只需找到k=m+n/2+1的数即可,其中k代表的是两数组所有数中由小到大取到的第k个数
  • 当m+n为偶数时需要找到k=m+n/2 k= m+n/2+1,然后再对两值的和折半即为结果

再来讨论一下二分法,二分法的核心就是通过缩小划分区间,找到目标元素,对本题来说用例如下
在这里插入图片描述
上述情况为一般的二分法查找过程,对于本题来说还需要考虑到两种极端情况,就是当两数组元素个数差距较大时,且元素少的数组中元素较小时,在查找过程中就会出现走到较小数组末尾的情况,此时上述方法就会导致越界访问,故需要单独处理,用例如下
在这里插入图片描述
思路讲完了,以下是代码实现

class Solution {
public:
    double Binarysearch(vector<int>& nums1, vector<int>& nums2, int midIndex) {
        int index1 = 0;
        int index2 = 0;
        int m = nums1.size();
        int n = nums2.size();
        while (true)
        {
            //当一边数组已经全部排除时
            if (index2 == nums2.size())
            {
                return nums1[index1 + midIndex - 1];
            }
            if (index1 == nums1.size())
            {
                return nums2[index2 + midIndex - 1];
            }
            //当无法继续二分时
            if (midIndex == 1)
            {
                return min(nums1[index1], nums2[index2]);
            }
            //取每次需要比较的两下标
            int newIndex1 = midIndex / 2 - 1 + index1;
            int newIndex2 = midIndex / 2 - 1 + index2;
            bool flag = false;
            //判断是否超过数组大小
            if (newIndex1 > m - 1)
            {
                newIndex1 = m - 1;
                flag = true;
            }
            else if (newIndex2 > n - 1)
            {
                newIndex2 = n - 1;
                flag = true;
            }
            //更新策略
            if (nums1[newIndex1] >= nums2[newIndex2])
            {
                if (flag)
                {
                    midIndex -= newIndex2 - index2 + 1;
                }
                else
                {
                    midIndex -= midIndex / 2;
                }
                index2 = newIndex2 + 1;
            }
            else
            {
                if (flag)
                {
                    midIndex -= newIndex1 - index1 + 1;
                }
                else
                {
                    midIndex -= midIndex / 2;
                }
                index1 = newIndex1 + 1;
            }
        }
    }

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int length = nums1.size() + nums2.size();
        if (length % 2 != 0)
        {
            return Binarysearch(nums1, nums2, length / 2 + 1);
        }
        else
        {
            return (Binarysearch(nums1, nums2, length / 2) + Binarysearch(nums1, nums2, length / 2 + 1)) / 2;
        }
        return 0;
    }
 };

实际上k值的更新本质上就是减去上次排除的元素个数,不需要区分情况处理,故最终简化代码如下:

class Solution {
public:
    double Binarysearch(vector<int>& nums1, vector<int>& nums2, int midIndex) {
        int index1 = 0;
        int index2 = 0;
        int m = nums1.size();
        int n = nums2.size();
        while (true)
        {
            //当一边数组已经全部排除时
            if (index2 == nums2.size())
            {
                return nums1[index1 + midIndex - 1];
            }
            if (index1 == nums1.size())
            {
                return nums2[index2 + midIndex - 1];
            }
            //当无法继续二分时
            if (midIndex == 1) 
            {
                return min(nums1[index1], nums2[index2]);
            }
            //取每次需要比较的两值,不能超过数组大小
            int newIndex1 = min(midIndex / 2 - 1 + index1, m - 1);
            int newIndex2 = min(midIndex / 2 - 1 + index2, n - 1);

            if (nums1[newIndex1] >= nums2[newIndex2])
            {
                midIndex -= newIndex2 - index2 + 1;
                index2 = newIndex2 + 1;
            }
            else
            {
                midIndex -= newIndex1 - index1 + 1;
                index1 = newIndex1 + 1;
            }
        }
    }

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int length = nums1.size() + nums2.size();
        if (length % 2 != 0)
        {
            return Binarysearch(nums1, nums2, length / 2 + 1);
        }
        else
        {
            return (Binarysearch(nums1, nums2, length / 2) + Binarysearch(nums1, nums2, length / 2 + 1)) / 2;
        }
        return 0;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值