【LeetCode】4. Median of Two Sorted Arrays

题目描述:

给定两个已排序的整数数列(长度分别为m和n),求出他们的中位数。要求时间复杂度为O(log (m+n)).

如:

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

The median is 2.0


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

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


题目分析:

由于题目要求时间复杂度为O(log (m+n)), 看到log这个运算符,很容易想到要使用分治算法。

可以把题目进行转换:找到两个有序数列的第k个元素。

对于合并数列长度为奇数的,其中位数为合并后中间的数字;对于合并数列长度为偶数的,其中位数为合并后中间的两个数字的平均值。


典型的分治算法如二分法,每次选择中间的元素,与需要查找的元素进行比较,然后可以缩减一半的查找范围,时间复杂度为O(log(n)),每次减半的变量为搜索区间的长度。


对于本道题,首先可以想,需要将哪一个变量进行减半。可能能减半的变量有:两个序列的长度,查找合并数列的第k个元素的k.

这里选择从k入手,对k实行减半。


图一


为了简化计算,假设第一个数列的长度小于等于第二个数列的长度

我们从第一个数列尽可能选取k/2个数,sa = min(k/2, alen), alen为nums1上的查找长度,查找长度将为减少,记取出的数的最后一个数为A.

然后从第二个数列中取出k - sa个数,记取出的最后一个数为B

取出的数总长度为k


当A < B时,合并后的数列为:

图二


其中,点A前面黄色的部分为<=A的数,长度小于k; 而橙色部分为>=A且<=B的数。

在图一的第一个数列中,由于A<B, A后面还可能存在着<B的数,因此图二橙色部分的长度大于等于0

故黄色加橙色的长度大于等于k

所以第k大数在橙色部分,我们可以得知黄色部分肯定不存在第k大数,故可以在下次搜索时忽略黄色段。

但黄色段包括数列一和数列二的元素,要实现起来有点麻烦。

由有序性,黄色段包含了点A前面的元素,由之前的取法知这些元素个数尽可能接近k/2.

因此可以将k减半。


不失一般性A > B时也类似处理。


当A = B时,说明正好找到了第k大元素。


如何忽略前面的元素,可以记每次搜索的序列为nums1[i, nums1.size()), nums2[j, nums2.size())


示例代码:

class Solution {
public:

int kth(vector<int> &nums1, int i, vector<int> &nums2, int j, int k){
	// [i, nums1.size())
	// [j, nums2.size())
	int alen = nums1.size() - i;
	int blen = nums2.size() - j;
	if (alen > blen)return kth(nums2,j,nums1,i,k);
	if (alen == 0)return nums2[j + k - 1];
	if (k == 1)return min(nums1[i], nums2[j]);
	int sa = min(k / 2,alen);
	int sb = k - sa;
	int pa = i + sa - 1;
	int pb = j + sb - 1;
	if (nums1[pa] < nums2[pb])return kth(nums1, pa + 1, nums2, j, k - sa);
	if (nums1[pa] > nums2[pb])return kth(nums1, i, nums2, pb + 1, k - sb);
	return nums1[pa];
}

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int alen = nums1.size();
        int blen = nums2.size();
        int mid = (alen + blen) / 2;
        if ((alen + blen) & 1){
            //奇数
            return kth(nums1, 0, nums2, 0, mid + 1);
        }
        //偶数
        return (kth(nums1, 0, nums2, 0, mid) + kth(nums1, 0, nums2, 0, mid + 1))/ 2.0; 
    }
};



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值