LeetCode4. Median of Two Sorted Arrays---vector实现O(log(m+n)--- findkth
这道题目和PAT上的1029是同一题。但是PAT1029用O(m+n)的时间复杂度(题解)就可以,这道题要求是O(log(m+n))。
这道题花费了我一个工作日的时间来思考。因为是log因而一直思考如何进行二分,想着是两个vector分别二分,但一直想不到合适的解法,就是无穷尽的if else
后来看了题解。思路是从两个有序数组中寻找第k小的数。
<1>花了很长时间在调试上,结果发现是少了一个return,期间还不停怀疑自己写递归的能力。。。要被自己打败了,调代码真是一件细小却很耗费心神的事情。
<2>对vector的使用也是醉了。
思路如下:
我们先假设nums1和nums2数组中元素个数都是大于 k/2 的,且从 0 开始编号,那么我们比较 a = nums1[k/2 - 1] 和 b = nums2[k/2 - 1]。
(1)如果 a < b 那么 nums1[0] 到 nums1[k/2 - 1] 这 k/2 个数在合并后的有序数组中,一定在第 k 小的数左边。为什么呢?
我们发现,nums1数组中比 a 小的数一共是 k/2 - 1 个。nums2数组中比 a 小的数最多有 k/2 - 1 个。因而合并以后比 a 小的数最多有k/2 - 1 + k/2 - 1 < k - 2。
也就是说 a 最多是第 k-1 小的数。所以说nums1数组前 k/2 个数可以剔除了。
(2)如果 a > b 同理,剔除掉 nums2数组前 k/2 个数。
(3)如果 a = b,a 即为所求。
每次剔除掉 k 一半个元素,因而时间复杂度是O(logk),对于此题,k = (m+n)/2 所以时间复杂度是O(log(m+n))
考虑实际情况:
如果nums1和nums2数组中元素个数不足 k/2 个的话,一般情况下,我们只需要满足两者前面的总数目为 k 即可,原理和上述的原理是类似的,不再赘述。
此外还需要考虑特殊情况,
(1)一个数组为空,则放回另一个数组的第 k 大;
(2)k==1则直接返回min(nums1[0],nums2[0])。
在实际实现的时候,由于输入是vector,它是一个动态数组,可以实时增加或删除元素,但是无法向普通数组/指针一样任意指定起始位置和结束为止,因而只好使用了删除操作
erase(iter1,iter2),删除[iter1,iter2)之间的元素。
1 class Solution { 2 public: 3 double findkth(vector<int>& nums1,vector<int>& nums2, int k) 4 { 5 int m = nums1.size(),n = nums2.size(); 6 if(m > n) 7 return findkth(nums2,nums1,k);//error 1. forget the "return"; 8 if(m == 0) 9 return double(nums2[k - 1]);//error 2. write as nums2[n - 1]; 10 if(k == 1) 11 { 12 return double(min(nums1[0],nums2[0])); 13 } 14 int pa = min(k / 2,m), pb = k - pa; 15 if(nums1[pa - 1] < nums2[pb - 1]) 16 { 17 nums1.erase(nums1.begin(),nums1.begin() + pa); 18 return findkth(nums1,nums2,k - pa); 19 } 20 else if(nums1[pa - 1] > nums2[pb - 1]) 21 { 22 nums2.erase(nums2.begin(),nums2.begin() + pb); 23 return findkth(nums1,nums2,k - pb); 24 } 25 else 26 return double(nums1[pa - 1]); 27 } 28 double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { 29 int len1 = nums1.size(),len2 = nums2.size(); 30 int len = len1 + len2; 31 vector<int> cop1(nums1),cop2(nums2); 32 if(len % 2) 33 { 34 return findkth(nums1,nums2,len / 2 + 1); 35 } 36 else 37 { 38 double t1=findkth(nums1,nums2,len / 2),t2=findkth(cop1,cop2,len / 2 + 1); 39 return (t1 + t2) / 2; 40 } 41 } 42 };
删除vector的元素需要①额外备份一下数组,②删除操作。无谓的耗时,因而重新实现成手动标记数组的起始位置。只需要在上面实现方法的基础上 + star就可以了,原理都是一样的。
1 class Solution { 2 public: 3 double findkth(vector<int>& nums1,int st1,int ed1,vector<int>& nums2, int st2,int ed2,int k) 4 { 5 int m = ed1 - st1,n = ed2 - st2; 6 if(m > n) 7 return findkth(nums2,st2,ed2,nums1,st1,ed1,k);//error 1. forget the "return"; 8 if(m == 0) 9 return double(nums2[st2 + k - 1]);//error 2. write as nums2[n - 1]; 10 if(k == 1) 11 { 12 return double(min(nums1[st1],nums2[st2])); 13 } 14 int pa = min(k / 2,m), pb = k - pa; 15 if(nums1[st1 + pa - 1] < nums2[st2 + pb - 1]) 16 { 17 return findkth(nums1,st1 + pa,ed1,nums2,st2,ed2,k - pa); 18 } 19 else if(nums1[st1 + pa - 1] > nums2[st2 + pb - 1])//error 3. forget the "st2 +"; 20 { 21 return findkth(nums1,st1,ed1,nums2,st2 + pb,ed2,k - pb); 22 } 23 else 24 return double(nums1[st1 + pa - 1]); 25 } 26 double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { 27 int len1 = nums1.size(),len2 = nums2.size(); 28 int len = len1 + len2; 29 if(len % 2) 30 { 31 return findkth(nums1,0,len1,nums2,0,len2,len / 2 + 1); 32 } 33 else 34 { 35 double t1 = findkth(nums1,0,len1,nums2,0,len2,len / 2),t2 = findkth(nums1,0,len1,nums2,0,len2,len / 2 + 1); 36 return (t1 + t2) / 2; 37 } 38 } 39 };