Leetcode#4.Median of Two Sorted Arrays(归并排序&二分查找求第k小的数)

声明:题目解法使用c++和Python两种,重点侧重在于解题思路和如何将c++代码转换为python代码。

Median of Two Sorted Arrays

There are two sorted arrays nums1 and nums2 of size m and n respectively.

Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

Example 1:

nums1 = [1, 3]
nums2 = [2]

The median is 2.0

Example 2:

nums1 = [1, 2]
nums2 = [3, 4]

The median is (2 + 3)/2 = 2.5

题意

给你两个有序数组,让你找出两数组并集后的中位数。要求时间复杂度为O(log(m+n))

思路一(最笨的方法) :合并数组+sort排序

这是最笨的方法,先把数组合并为一个数组,就需要O(n+m)的时间,然后再sort排序,首先我们知道,C++中的sort()则是改进的快排算法,时间复杂度是nlog(n),对于我们题意来说则是(n+m)log(n+m)。代码很简单就不写了。

思路二:归并排序(加优化)

从题意中我们可以得到的信息为:有序的数组,想要得出中位数,肯定要把两个有序的数组合成一个有序的数组。这时候很容易想到归并排序(Merge Sort)的定义:

  • 归并:将两个有序数列合并成一个有序数列。
  • 归并排序:利用归并思想对数列进行排序。

归并排序的时间复杂度为:O(n)

考虑题目中要求的是中位数,我们只需归并到第(n+m)/2个元素。
时间复杂度为O((n+m)/2)。
归并排序代码:

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {

        //归并排序
        int m = nums1.size(), n = nums2.size(),mid;
        int i = 0, j = 0, k = 0;//合并后的数组
        mid =(m+n)/2;
        vector<int>res(m+n+10,0);

        while(i<m || j<n)
        {
            if(i==m)
            {
                res[k++] = nums2[j];
                j++;
            }
            else if(j==n)
            {
                res[k++] = nums1[i];
                i++;
            }
            else if(nums1[i]<=nums2[j])
            {
                res[k++] = nums1[i];
                i++;
            }
            else if(nums1[i]>nums2[j])
            {
                res[k++] = nums2[j];
                j++;
            }
            //遍历到数组的m+n/2的
            if(k>mid) 
                break;
        }
        if((m+n)&1)
        return (res[mid-1]+res[mid])*1.0/2; 
        else
            return res[mid];
    }
};

虽然在leetcode上提交成功了,但是还是没有达到题目的要求的时间复杂度O(log(m+n))。(怪不得第一道题目我暴力也能过,果然leetcode里面的时间复杂度卡的不严),

思路三:第k小元素(二分的思想)

最近在网上看到了一种非常好的方法:
这里将问题化为一般性:如何找到两个有序数组中第k大的元素?
首先要知道二分查找的基本思想
二分查找的基本思想是将n个元素分成大致相等的两部分,去a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;如果x

大意:

假设A和B是两个元素个数都超过k/2的数组,如果我们要比较A和B数组中的第k/2个元素的值,则会得出三种结果(因为k可能是偶数或者奇数,在这里为了简化问题,我们假设是奇数)

A[K/2-1] > B[K/2-1]
A[K/2-1] < B[K/2-1]
A[K/2-1] = B[K/2-1]

如果 A[k/2-1] < B[k/2-1],意味着从A[0]~A[k/2-1]都小于A和B数组合并后的第k小数,换句话说A[k/2-1]不可能大于两数组合并后的第k小数。
为什么呢?
我们可以用反证法证明:假设A[k/2-1]大于合并后的第k小数,就假设是第k+1小数,因为A[k/2-1]

我们也应该考虑边界的问题:
  • A与B其中一个为空,返回A[k-1]或者B[k-1]。
  • k=1时(A和B都不为空),返回min(A[0],B[0])。
  • A[k/2-1]=B[k/2-1],返回其中一个。
    实现的代码:
class Solution {
public:
    double findkth(vector<int>::iterator a, int m, vector<int>::iterator b, int n, int k)
    {
        //始终保持a数组的元素个数最少
        if(m > n)
            return findkth(b, n, a, m, k);
        //其中一个为0
        if(m == 0)
            return b[k-1];
        //元素都为1
        if(k == 1)
            return min(*a, *b);

        //因为a数组是元素少的数组,元素可能没有k/2个,所以不能用
        //int pa = k / 2 可能导致数组溢出
        int pa = min(k/2, m), pb = k-pa;
        //丢弃a
        if(*(a + pa - 1) < *(b + pb-1))

            return findkth(a + pa, m - pa, b, n, k-pa);

        //丢弃b
        else if(*(a + pa-1) > *(b + pb - 1))

            return findkth(a, m, b + pb, n - pb, k - pb);

        //相等直接返回
        else 
            return *(a + pa-1);
    }
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {

        vector<int>::iterator a = nums1.begin();
        vector<int>::iterator b = nums2.begin();
        int m = nums1.size(), n = nums2.size();
        int len = m + n;
        if(len & 1)//偶数
            return findkth(a, m, b, n, len/2 + 1);
        else//奇数第k小元素+第k+1小元素/2
            return (findkth(a, m, b, n, len/2 + 1) + findkth(a, m, b, n, len/2)) / 2;
    }

};

我们知道二分查找的时间复杂度为O(logn)(以2为底),题目中我们要找的是第n+m/2小值,所以时间复杂度为O(log(n+m))达到题目的要求。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值