时间O(log(m+n)) 空间O(1)
题目要求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个元素。递归的终止条件有如下几种:
<pre name="code" class="java">private static double findKth(int[] nums1, int s1, int len1, int[] nums2, int s2, int len2, int k){ if(len1>len2){//保证nums1为短的那个 return findKth(nums2, s2, len2, nums1, s1, len1, k); } if(len1==0){//短数组中元素都被抛弃,返回长数组中第k个元素即nums2[s2+k-1] return nums2[s2+k-1]; } if(k==1){ return java.lang.Math.min(nums1[s1], nums2[s2]); } int p=java.lang.Math.min(k/2, len1); int q=k-p; if(nums1[s1+p-1]<nums2[s2+q-1]){ return findKth(nums1, s1+p, len1-p, nums2, s2, len2, k-p); }else if(nums1[s1+p-1]>nums2[s2+q-1]){ return findKth(nums1, s1, len1, nums2, s2+q, len2-q, k-q); }else{ return nums1[s1+p-1]; } } public static double findMedianSortedArrays(int[] nums1, int[] nums2) { int k=nums1.length+nums2.length; if((k&1)==1){ return findKth(nums1, 0, nums1.length, nums2, 0, nums2.length, k/2+1); }else{ return (findKth(nums1, 0, nums1.length, nums2, 0, nums2.length, k/2)+findKth(nums1, 0, nums1.length, nums2, 0, nums2.length, k/2+1))/2; } }