leetcode journal《微软题库》-寻找两个正序数组的中位数

题目:给定两个大小分别为 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

提示:

  • 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),使用归并的思想将两个容器中的数据按照从小到大的顺序存放到一个容器中,得到有序的总数据后再取中位数。整个算法通过遍历两个容器来实现,因此时间复杂度为O(m+n),但是还有进一步优化的空间。

算法2:

​ 根据题目的要求,时间复杂度应为O(log (m+n)),一般来说,对于log级别的时间复杂度,我们应该用到二分法的思想,在这道题中也一样,无需复制容器中的数据,而是通过二分法的思想来进行求解。

​ 考虑两个有序数组AB,需要找第k个元素,那么我们可以比较A[k/2-1]B[k/2-1]A[k/2−1]B[k/2−1] 的前面分别有 A[0,...,k/2-2]和B[0…k/2−2]这些元素,分别是k/2-1个元素,他们肯定不会是第k个元素;而对于A[k/2-1]B[k/2-1],他们之中较小的那一个是第k/2-1个元素,也一定不是第k个元素。

  • ​ 由此我们来看一下三种情况:

    • A[k/2-1]<B[k/2-1],那么比A[k/2−1]小的数只有 2 × ( k / 2 − 1 ) = k − 2 2\times (k/2-1)=k-2 2×(k/21)=k2个,因此A[k/2−1]必然不是第k个数,前面的数也可排除

    • A[k/2-1]>B[k/2-1],和上一种情况对称,B[k/2−1]必然不是第k个数,前面的数也可排除

    • A[k/2-1]=B[k/2-1],归入第一种情况处理

​ 可以看到通过这种方式,我们可以缩小一半的查找范围。在排除一些数据的基础上,我们可以继续进行二分查找,并且随着查找的过程,k的值会一直减小,最终排除所有不大于第k小的数。

​ 在排除数据的时候,我们需要考虑以下三种情况:

1. 如果A[k/2−1] 或者B[k/2−1]越界,那么我们可以选取对应数组中的最后一个元素。在这种情况下,我们必须根据排除数的个数减少k的值,而不能直接将k减去k/2。
2. 如果一个数组为空,说明该数组中的所有元素都被排除,我们可以直接返回另一个数组中第k小的元素。
3. 如果k=1,我们只要返回两个数组首元素的最小值即可。

代码:

算法1
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        vector<int> combined;
        int index1=0,index2=0;
        while(index1<nums1.size()||index2<nums2.size())
        {
            while(index1==nums1.size()&&index2<nums2.size())
            {
                 combined.push_back(nums2[index2++]);
            }
            while(index2==nums2.size()&&index1<nums1.size())
            {
                 combined.push_back(nums1[index1++]);
            }
            while(index1<nums1.size()&&index2<nums2.size()&&nums1[index1]==nums2[index2])
            {
                combined.push_back(nums1[index1++]);
                combined.push_back(nums2[index2++]);
            }
            while(index1<nums1.size()&&index2<nums2.size()&&nums1[index1]<nums2[index2])
            {
                combined.push_back(nums1[index1++]);
            }
            while(index1<nums1.size()&&index2<nums2.size()&&nums1[index1]>nums2[index2])
            {
                combined.push_back(nums2[index2++]);
            } 
        }

        int len=combined.size()-1;
        if(len%2==0)
        {
            return 1.0*combined[len/2];
        }
        else
        {
            double res=1.0*(combined[(len+1)/2]+combined[(len-1)/2])/2.0;
            return res;
        }
    }
};
算法2
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int len=nums1.size()+nums2.size();
        if(len%2==1)
        {
            return k_elemnt(nums1,nums2,(len+1)/2);
        }
        else
        {
            return (k_elemnt(nums1,nums2,len/2)+k_elemnt(nums1,nums2,len/2+1))/2.0;
        }
    }
    int k_elemnt(vector<int>& nums1, vector<int>& nums2,int k)
    {
        int index1=0,index2=0;
        while(true)
        {
            //边界情况
            if(index1==nums1.size())//nums1全部排除,直接从nums2找第k小
            {
                return nums2[index2+k-1];
            }
            if(index2==nums2.size())//nums2全部排除,直接从nums1找第k小
            {
                return nums1[index1+k-1];
            }
            if(k==1)//直接找最小
            {
                return min(nums1[index1],nums2[index2]);
            }

            int tmp_id1=index1+k/2-1<nums1.size()-1?index1+k/2-1:nums1.size()-1;//在index1的基础上更新第k/2-1个数的下标
            int tmp_id2=index2+k/2-1<nums2.size()-1?index2+k/2-1:nums2.size()-1;//在index2的基础上更新第k/2-1个数的下标
            if(nums1[tmp_id1]<=nums2[tmp_id2])
            {
                k-=tmp_id1-index1+1;//在index1的基础上减小k的值
                index1=tmp_id1+1;
            }
            else
            {
                k-=tmp_id2-index2+1;//在index2的基础上减小k的值
                index2=tmp_id2+1;
            }
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值