题面
难度标识: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个元素。递归的终止条件有如下几种:
- 较短序列所有元素都被抛弃,则返回较长序列的第k个元素(在数组中下标是k-1)
- 取得的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:注意元素序号和长度等具有实际意义的量和数组下标的关系,加一减一什么的很烦人