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

二分查找


题目详情

给定两个大小分别为 mn的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数
算法的时间复杂度应该为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

我的第一种代码(直接解法):

思路:直接合并两个数组,然后sort一下,分情况找中位数

class Solution 
{
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) 
    {
        vector<int> nums;
        int len1=nums1.size(),len2=nums2.size();
        //这里可以用insert来合并两个数组
        for(int i=0;i<len1;++i)
        {
            nums.push_back(nums1[i]);
        }
        for(int j=0;j<len2;++j)
        {
            nums.push_back(nums2[j]);
        }
        sort(nums.begin(),nums.end());
        int len=nums.size();
        if(len%2!=0)
        return nums[len/2];
        else
        {
        //注意这里的结果平均数要强制类型转换成浮点数
        return (double)(nums[len/2-1]+nums[len/2])/2;
        }
    }
};

上面这种解法直接简单能通过,但是复杂度不满足题目要求,题目要求明显是二分查找,太难想了,我看了几遍官方讲解视频后总结出了我的代码:

//看完官方讲解之后自己总结的代码及解释(看完官方题解视频再来看)
class Solution 
{
public:
double findMedianSortedArrays(vector<int>& nums1,vector<int>& nums2)
{
    if (nums1.size() > nums2.size())    //为了方便操作,在较短的的数组上进行大多数操作可以减少大多数情况下的算法耗时
    { 
        vector<int> temp = nums1;
        nums1 = nums2;
        nums2 = temp;
    }
    int m=nums1.size(),n=nums2.size();
    int minI = 0;               //这里也就是nums1划分线的左边位置下标可能的最小值
    int maxI = m;                //最大值
    int halfLen = (m + n + 1) / 2;//官方题解有解释,halfLen即为划分线左右总体的元素个数(+1是因为免去讨论奇偶) 
    while (minI <= maxI)              //二分查找
    {
        int i = (minI + maxI) / 2;  //在可能的范围内用二分查找法来找到正确的数值,这个是中间值
        int j = halfLen - i;        //由nums1的确定位置来得出nums2的位置j
        if (i < maxI && nums2[j - 1] > nums1[i]) //如果i没有向右越界 且 nums2划分线的左边元素大于交叉对应那个nums1元素了(不懂去看官方题解)
        {
            minI = i + 1; // i 这个中间值小了,我们调整 i 的范围(调整划分线)
        }
        else if (i > minI && nums1[i - 1] > nums2[j])//如果i没有向左越界 且 nums1划分线的左边大于交叉对应那个nums2元素了(↑)
        {
            maxI = i - 1; //  i 这个中间值大了,我们调整 i 的范围(调整划分线)
        }
        else //i值是正确的值(划分线满足条件)找出左侧最大值,再分奇偶看看是否需要右侧最小值
        {
            int maxLeft; //找到左侧最大值
            if (i == 0)     //nums1的i左边没有元素了
            {
                maxLeft = nums2[j - 1]; 
            }
            else if (j == 0) //nums2的j左边没有元素了
            {
                maxLeft = nums1[i - 1]; 
            }
            else 
            {
                maxLeft = max(nums1[i - 1], nums2[j - 1]); 
            }
            if ((m + n) % 2 == 1)         //如果是奇数,则中位数就是maxLeft了,直接返回
            { 
                return maxLeft; 
            }

            int minRight; //找到右侧最小值    如果是偶数还需要找到minRight求平均数
            if (i == m)                 //num1的i右边没有元素了
            {
                minRight = nums2[j]; 
            }
            else if (j == n)            //nums2的j右边没有元素了
            {
                minRight = nums1[i]; 
            }
            else 
            { 
                minRight = min(nums2[j], nums1[i]); 
            }

            return (double)(maxLeft + minRight) / 2.0;
        }
    }
    return 0.0;
}
};

涉及知识点:

1.二分查找

二分查找也常被称为二分法或者折半查找,每次查找时通过将待查找区间分成两部分并只取一部分继续查找,将查找的复杂度大大减少。对于一个长度为 O(n) 的数组,二分查找的时间复杂度为 O(log n)。

posted @ 2022-03-31 17:07  ggaoda  阅读(3)  评论(0编辑  收藏  举报  来源