[LeetCode] 两个有序数组的中位数

题面

难度标识:Hard

        题目意思就是给定两个排好序的数组,求合并之后的中位数。本来觉得非常简单,用归并中合并两有序数组的写法就搞定

后来发现果然标的hard不是随意标的,要求复杂度是log(m+n)。似乎本题对C++不太友好,因为别人写的java,O(m+n)的很

水的代码好像也能过。

思路

       题目要求O(log(m+n))的时间复杂度,一般来说都是分治法或者二分搜索。首先我们先分析下题目,假设两个有序序列共有n个元素(根据中位数的定义我们要分两种情况考虑),当n为奇数时,搜寻第(n/2+1)个元素,当n为偶数时,搜寻第(n/2+1)和第(n/2)个元素,然后取他们的均值。进一步的,我们可以把这题抽象为“搜索两个有序序列的第k个元素”。如果我们解决了这个k元素问题,那中位数不过是k的取值不同罢了。
        那如何搜索两个有序序列中第k个元素呢?因为序列都是从小到大排列,对于第一个序列中前p个元素和第二个序列中前q个元素,我们想要的最终结果是:p+q等于k-1,且一序列第p个元素和二序列第q个元素都小于总序列第k个元素。因为总序列中,必然有k-1个元素小于等于第k个元素。这样第p+1个元素或者第q+1个元素就是我们要找的第k个元素。

所以,我们可以通过二分法将问题规模缩小,假设p=k/2-1,则q=k-p-1,且p+q=k-1。如果第一个序列第p个元素小于第二个序列第q个元素,我们不确定二序列第q个元素是大了还是小了,但一序列的前p个元素肯定都小于目标,所以我们将第一个序列前p个元素全部抛弃,形成一个较短的新序列。然后,用新序列替代原先的第一个序列,再找其中的第k-p个元素(因为我们已经排除了p个元素,k需要更新为k-p),依次递归。同理,如果第一个序列第p个元素大于第二个序列第q个元素,我们则抛弃第二个序列的前q个元素。递归的终止条件有如下几种:

  1. 较短序列所有元素都被抛弃,则返回较长序列的第k个元素(在数组中下标是k-1)
  2. 取得的k=1时此时求两数组剩余元素组成序列的第一个元素,即min(nums1[start1], nums2[start2])
代码如下:
#include <algorithm>
#include <cmath>

double findKth(vector<int> &nums1, int start1, int len1,
			   vector<int> &nums2, int start2, int len2,
			   int k)
{
	//保证nums1是长度比较小的数组
	if(len1>len2)
		return findKth(nums2, start2, len2,nums1, start1, len1,k);

    //nums1长度为0时,直接从nums2中找
	if(len1==0)
		return 1.0*nums2[start2+k-1];
	//取得是第一个元素时,返回两个数组第一个元素较小值
	if(k==1)
		return 1.0*min(nums1[start1],nums2[start2]);

	//nums1是长度较小的,若k/2超过len1,则取len1
	int q1=min(k/2,len1);
	
	int q2=k-q1;

	if(nums1[start1+q1-1]<nums2[start2+q2-1])
		return findKth(nums1,start1+q1,len1-q1,nums2,start2,len2,k-q1);
	else if(nums1[start1+q1-1]>nums2[start2+q2-1])
		return findKth(nums1,start1,len1,nums2,start2+q2,len2-q2,k-q2);
	else
		return 1.0*nums1[start1+q1-1];


}
class Solution 
{
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) 
    {    
        //十分丑陋,可以用临时变量使变得更清晰一些
        if((nums2.size()+nums1.size())%2!=0)
         	return findKth(nums1,0,nums1.size(),nums2,0,nums2.size(),
         		          (nums2.size()+nums1.size()+1)/2);
         else
         	return (findKth(nums1,0,nums1.size(),nums2,0,nums2.size(),
         		          (nums2.size()+nums1.size())/2)+
                   findKth(nums1,0,nums1.size(),nums2,0,nums2.size(),        		          
                   	(nums2.size()+nums1.size())/2+1))/2.0;
    }
};
Warning:注意元素序号和长度等具有实际意义的量和数组下标的关系,加一减一什么的很烦人
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值