给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的思路非常直接。答案的内存和时间使用情况也都很好,所以就先说自己的想法:(但是这个解法似乎并不能满足时间复杂度的要求)
考虑总共有三种情况:
1.nums1为空。分奇偶直接取数组nums2中位数。
2.nums2为空。分奇偶直接取数组nums1中位数。
3.nums1和nums2均非空。
和取数组中位数一样的思路,把这两个数组按序放进一个新的数组中,直接分奇偶按坐标取新数组的中位数。
遍历一遍自然是O(n+m),由于O(log(n+m))的要求计数统计到一半的时候能取得结果就停下来。
class Solution { public: double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { int len1=nums1.size(); int len2=nums2.size(); if(len1==0)//数组1为空 { if(len2%2==0)//分奇偶取中位数 return (nums2[len2/2-1]+nums2[len2/2])/2.0; else return nums2[len2/2]; } if(len2==0)//数组2为空 { if(len1%2==0) return (nums1[len1/2-1]+nums1[len1/2])/2.0; else return nums1[len1/2]; } //均非空 int len=len1+len2; int a[10000];//存归并的数组 int count=0; int p=0; int q=0; while(count<=len/2)//对每个测试用例只执行到中位数为止(一半) { if(p>=len1) a[count++]=nums2[q++];//数组1已经没有元素了 else if(q>=len2) a[count++]=nums1[p++];//数组2已经没有元素了 //不可能同时没有元素 else { if(nums1[p]<nums2[q]) a[count++]=nums1[p++]; else a[count++]=nums2[q++]; } } if(len%2==0) return (a[count-1]+a[count-2])/2.0; else return a[count-1]; return 0; } };
其实这个思路第三种情况也可以处理前两种,所以前两种代码可省去更美观。但是我写代码一直有贪心的习惯……而且20ms和32ms我觉得是值得写的。
二分思路:(推荐直接看上面链接的标解)
“中位数”在统计中:将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。
left_part | right_part
A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]
B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
如果在这两个数组中分别插一根线,就可以将两个数组各分为两部分,这根线的插法要满足:
1.两个数组的左侧部分长度之和始终等于右侧部分长度之和,因此每个i唯一确定一个j。 (i+j)×2=m+n+1
2.我们要做的就是不断移动这根线(i的取值)使得左侧部分的所有值小于右侧部分的所有值。由于数组有序,这样的线是必定存在的。
这两个条件就给出了全部的思路。i的取值为[0,m-1],根据i确定j,然后考虑线左边和右边共四个元素的大小。但是这样时间复杂度为O(min(m,n)),为了达到log级,我们加上第三条处理:
3.由于数组是顺序的,所以采用二分来选取i的值。
*最后记得奇偶的处理。