题面描述 寻找两个有序数组的中位数,要求时间复杂度 $ O\left( \log_2 \left(n + m \right) \right)$
给定两个单调非减的有序数组,保证不全为空。求这两个数组合并后的中位数,要求时间复杂度 $ O\left( \log_2 \left(n + m \right) \right)$ 。
最朴素的想法肯定是二分套二分,考虑到第二个二分的size也会快速递减,时间复杂度应该在小常数 $ O \left( \log_2 n \cdot \log_2 m \right) $ 。
事实上这已经是非常优秀的复杂度了。如果面试官面我这个问题,我会说在普通电脑能储存存的数据规模下,上述复杂度和要求复杂度在计算时间上可能差异不大,在工程的考量上可以采用前者,因为前者实现更简洁不易产生bug。
正解没有想到,看了题解脑海里才冒出”k-th element”这个词(虽然不太贴切)。最近题目刷少了,思维码力都不行啊。
然后就是繁琐的实现过程,可能写得不是太好。
- 考虑到两个有序数组求第 $k$ 大,分别取数组第 $\frac{k}{2}$ 个元素- 如果第一个数组的第 $\frac{k}{2}$ 个元素比较小,那表示第一个数组的前 $\frac{k}{2}$ 个元素都太小。抛弃这个部分,继续求剩下数组第 $\frac{k}{2}$ 小。
- 反之亦然
 
- 如果数组元素个数不够 $\frac{k}{2}$ 个,则比较最后一个值和另一个数组第 $\frac{k}{2}$ 个元素的大小- 如果最后一个值较小,那么直接从另一个数组里出答案
- 否则,抛弃另一个数组前 $\frac{k}{2}$ 个元素,继续求剩下第 $\frac{k}{2}$ 小。
 
所谓抛弃一部分,只涉及指针位置的修改而非物理意义上的删除。那么每次问题规模折半,从分治角度出发这就是单分规模支折半问题,经典的对数复杂度。
考虑到初始 $ k= \frac{m+n}{2}$ 故总复杂度 $ O\left( \log_2 \left(n + m \right) \right)$ 。
另外还有各种细节:
- 一个数组为空的时候要避免访问该数组
- 给小规模问题一个出口总是好的(比如 $k=0$ 求第0小,那么一定是最小的两个数中小的那个)
- 偶数个数的中位数问题(如果偷懒可以二分两遍,但一定有更好的做法)
| 1 | class Solution { | 
| 2 | protected: | 
| 3 |     int n,m; | 
| 4 |     int kth_elem(vector<int>& nums1,int L1,vector<int>& nums2,int L2,int k){ | 
| 5 |         if (L1==n) return nums2[L2+k]; | 
| 6 |         if (L2==m) return nums1[L1+k]; | 
| 7 |         if (k==0) return min(nums1[L1],nums2[L2]); | 
| 8 |         int nk=(k-1)/2; | 
| 9 |         if (n-L1<=nk){ | 
| 10 |             if (nums1[n-1]>=nums2[L2+nk]){ | 
| 11 |                 return kth_elem(nums1,L1,nums2,L2+nk+1,k-nk-1); | 
| 12 |             } | 
| 13 |             return nums2[L2+k-(n-L1)]; | 
| 14 |         } | 
| 15 |         if (m-L2<=nk){ | 
| 16 |             if (nums2[m-1]>=nums1[L1+nk]){ | 
| 17 |                 return kth_elem(nums1,L1+nk+1,nums2,L2,k-nk-1); | 
| 18 |             } | 
| 19 |             return nums1[L1+k-(m-L2)]; | 
| 20 |         } | 
| 21 |         if (nums1[L1+nk]>nums2[L2+nk]){ | 
| 22 |             return kth_elem(nums1,L1,nums2,L2+nk+1,k-nk-1); | 
| 23 |         } | 
| 24 |         return kth_elem(nums1,L1+nk+1,nums2,L2,k-nk-1); | 
| 25 |     } | 
| 26 | public: | 
| 27 |     double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { | 
| 28 |         n=nums1.size(),m=nums2.size(); | 
| 29 |         double ans1=1.0*kth_elem(nums1,0,nums2,0,(n+m)/2); | 
| 30 |         if ((n+m)%2==1) return ans1; | 
| 31 |         double ans2=1.0*kth_elem(nums1,0,nums2,0,(n+m)/2-1); | 
| 32 |         return (ans1+ans2)/2.0; | 
| 33 |     } | 
| 34 | }; | 
Comment: 最近Leetcode推出每日一题的活动,3月1日开始,我准备坚持参加一下