There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
You may assume nums1 and nums2 cannot be both empty.
Example 1:
nums1 = [1, 3] nums2 = [2] The median is 2.0
Example 2:
nums1 = [1, 2] nums2 = [3, 4] The median is (2 + 3)/2 = 2.5
思路:
中位数,其实就是找到第k个大小的元素的特例。在单数组中实现方式简单,关键是如何在两个数组中找到第k大的元素。难就难在要在两个未合并的有序数组之间使用二分法,这里我们需要定义一个函数来找到第K个元素,由于两个数组长度之和的奇偶不确定,因此需要分情况来讨论,对于奇数的情况,直接找到最中间的数即可,偶数的话需要求最中间两个数的平均值。下面重点来看如何实现找到第K个元素,首先我们需要让数组1的长度小于或等于数组2的长度,那么我们只需判断如果数组1的长度大于数组2的长度的话,交换两个数组即可,然后我们要判断小的数组是否为空,为空的话,直接在另一个数组找第K个即可。还有一种情况是当K = 1时,表示我们要找第一个元素,只要比较两个数组的第一个元素,返回较小的那个即可。
首先假设数组A和B的元素个数都大于k/2,我们比较A[k/2-1]和B[k/2-1]两个元素,这两个元素分别表示A的第k/2小的元素和B的第k/2小的元素。这两个元素比较共有三种情况:>、<和=。如果A[k/2-1]大于B[k/2-1],则A[k/2-1]小于合并之后的第k小值。
证明也很简单,可以采用反证法。假设A[k/2-1]大于合并之后的第k小值,我们不妨假定其为第(k+1)小值。由于A[k/2-1]小于B[k/2-1],所以B[k/2-1]至少是第(k+2)小值。但实际上,在A中至多存在k/2-1个元素小于A[k/2-1],B中也至多存在k/2-1个元素小于A[k/2-1],所以小于A[k/2-1]的元素个数至多有k/2+ k/2-2,小于k,这与A[k/2-1]是第(k+1)的数矛盾。
同理当A[k / 2 - 1] > B[k / 2 -1]时存在类似的结论
当A[k / 2 - 1] = B[k / 2 -1]时,表示,在在A的k/2 -1之前已经有k/2 -1和数小于A[k / 2 -1],同理在B 之前也是一样的,所以此时已经找到了第k小的数,即这个相等的元素。
1 class Solution { 2 public: 3 double findKth(vector<int>& nums1, int p1, vector<int>& nums2, int p2, int k){ 4 int len1 = nums1.size(), len2 = nums2.size(); 5 if(len1-p1>len2-p2){ 6 return findKth(nums2,p2,nums1,p1,k); 7 } 8 if(len1==p1){ 9 return nums2[p2+k-1]; 10 } 11 if(k==1){ 12 return min(nums1[p1],nums2[p2]); 13 } 14 int l1 = min(k/2 + p1,len1), l2 = p2 + k - l1 + p1;; 15 int max1 = nums1[l1-1], max2 = nums2[l2-1]; 16 if(max1==max2) return max1; 17 else if(max1<max2){ 18 return findKth(nums1,l1,nums2,p2,k-l1+p1); 19 } 20 else { 21 return findKth(nums1,p1,nums2,l2,k-l2+p2); 22 } 23 } 24 double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { 25 int len1 = nums1.size(),len2 = nums2.size(); 26 if((len1+len2)%2==0){ 27 return (findKth(nums1,0,nums2,0,(len1+len2)/2)+findKth(nums1,0,nums2,0,(len1+len2+2)/2))/2; 28 } 29 else{ 30 return findKth(nums1,0,nums2,0,(len1+len2+1)/2); 31 } 32 } 33 };
#下面这个代码思路完全一样,加了一些注释,是来自别人的博客,附上原地址,感谢大佬:https://www.cnblogs.com/mini-coconut/p/9066508.html
1 double findKth(vector<int> &nums1, int i, vector<int> &nums2, int j, int k) 2 { 3 // 首先需要让数组1的长度小于或等于数组2的长度 4 if (nums1.size() - i > nums2.size() - j) { 5 return findKth(nums2, j, nums1, i, k); 6 } 7 // 判断小的数组是否为空,为空的话,直接在另一个数组找第K个即可 8 if (nums1.size() == i) { 9 return nums2[j + k - 1]; 10 } 11 // 当K = 1时,表示我们要找第一个元素,只要比较两个数组的第一个元素,返回较小的那个即可 12 if (k == 1) { 13 return min(nums1[i], nums2[j]); 14 } 15 int pa = min(i + k / 2, int(nums1.size())), pb = j + k - pa + i; 16 17 if (nums1[pa - 1] < nums2[pb - 1]) { 18 return findKth(nums1, pa, nums2, j, k - pa + i); 19 } 20 else if (nums1[pa - 1] > nums2[pb - 1]) { 21 return findKth(nums1, i, nums2, pb, k - pb + j); 22 } 23 else { 24 return nums1[pa - 1]; 25 } 26 } 27 double findMedianSortedArrays(vector<int> A, vector<int> B) { 28 int sizeA = A.size(), sizeB = B.size(); 29 if (sizeA <= 0 && sizeB <= 0) { 30 return 0; 31 } 32 int total = sizeA + sizeB; 33 if (total % 2 == 1) { 34 return findKth(A, 0, B, 0, total / 2 + 1); 35 } 36 else { 37 return (findKth(A, 0, B, 0, total / 2) + findKth(A, 0, B, 0, total / 2 + 1)) / 2; 38 } 39 }
这里比较难理解的点是判断(nums1[pa - 1] < nums2[pb - 1])之后执行了return findKth(nums1, pa, nums2, j, k - pa + i);其实这个操作是因为目前nums1的分界线的值小于nums2分界线的值,那么证明nums1分界线以及前面的值都小于合并后的第k的值,也就是中位数。那么我们可以从这里开始,继续寻找第k-(pa-i)的值,直到两个值相等为止。