【leetcode】4. Median of Two Sorted Arrays

34 篇文章 0 订阅
13 篇文章 0 订阅

题目如下:

已知两个已从小到大排序的数组nums1[M]和 nums2[N],返回两个数组中所有数字从小到大排序后的中位数。两个数组中可能均有重复的数字。

时间复杂度不允许超过log(N+M).

分析:

看到已排序的、可随机访问的数组,肯定用二分查找。

估计还是有人不知道中位数是什么。中位数就是:若总数为偶数,则返回中间两个数字的平均值;否则返回中间的那个数字。

二分查找的难点在于现在没有办法确定中间点的位置,因为数字分布在两个数组中。既然这样,我们就主动简化一下问题。寻找两个数组中从小到大的第k个数字。

在这里,将特殊问题泛化了,题目反而变得简单了。

当N+M为奇数时,我们要找的中位数为k = (N+M)/2+1。当N+M为偶数时,我们要找的中位数为k=(N+M)/2-1 和 k=(N+M)/2。

好,有了上面的分析,下面我们只用考察 double findKth函数即可。

double findKth(const int* nums1, int realSize1, const int* nums2, int realSize2, int k);
对于k,下面我们考察两个数组中的第k/2个元素的大小。无非三种情况,>、<、=。

当然,可能存在k/2超过数组大小的情况。如果超过其中某一个数组的大小,则本次取该数组的最后一个值,下一次的时候,只用考察另外一个数组就行了。

为避免出现越界问题,选让长度小的数组选择,之后,另外一个数组选择剩下的长度。

int p1 = 0, p2 = 0;
if (realSize1 < realSize2)
{
	p1 = std::min(k / 2, realSize1);
	p2 = k - p1;
}
else
{
	p2 = std::min(k / 2, realSize2);
	p1 = k - p2;
}
从上面的分法我们也可以看到,不用区分k为奇数还是偶数,因为划分好了一个,k剩下的所有长度全归另一个了。

1.当nums1[p1-1] > nums2[p2-1]时,我们可以确定num2数组中从nums2[0]到nums2[p2-1]这些数字肯定位于合并之后的第k个数字之前。所以这部分数字对我们没有用处。舍弃之。之后,我们就只用考察nums1[0...M] 和 nums2[p2...N]了。

2.当nums1[p1-1] < nums2[p2-1]时,我们可以确定num1数组中从nums1[0]到nums1[p1-1]这些数字肯定位于合并之后的第k个数字之前。所以这部分数字对我们没有用处。舍弃之。之后,我们就只用考察nums1[p1...M] 和 nums2[0...N]了。

3.当nums1[p1-1] == nums2[p2-1]时,我们可以确定,第k个数字必定出现在nums1[p1-1] 与nums2[p2-1] 这二者中,所以返回任一个均OK。

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int size1 = nums1.size();
        int size2 = nums2.size();
        int total = size1 + size2;
        if(total & 1)
            return findKth(nums1.data(), size1, nums2.data(), size2, total/2+1);
        else
        {
            double ret = findKth(nums1.data(), size1, nums2.data(), size2, total/2) + 
                findKth(nums1.data(), size1, nums2.data(), size2, total/2+1);
            return ret/2.0;
        }
    }
    
    double findKth(const int* nums1, int realSize1, const int* nums2, int realSize2, int k)
    {
        if(0 == realSize1)
            return nums2[k-1];
        else if(0 == realSize2)
            return nums1[k-1];
        
        if(1 == k)
            return nums1[0]>nums2[0]?nums2[0]:nums1[0];
        
        int p1 = 0, p2 = 0;
    	if (realSize1 < realSize2)
    	{
    		p1 = std::min(k / 2, realSize1);
    		p2 = k - p1;
    	}
    	else
    	{
    		p2 = std::min(k / 2, realSize2);
    		p1 = k - p2;
    	}
        if(nums1[p1 - 1] < nums2[p2 - 1])
            return findKth(nums1+p1, realSize1 - p1, nums2, realSize2, k - p1);
        else if(nums1[p1 - 1] > nums2[p2 - 1])
            return findKth(nums1, realSize1, nums2+p2, realSize2-p2, k - p2);
        else
            return nums1[p1 -1];
    }
};
有人考虑不用递归来做,其实这里使用递归只是为了避免繁琐的边界判断问题。如果用迭代,则返回时,要区分k为奇偶,同时p1 p2是取的k/2值,还是取的realSize1、realSize2。麻烦。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值