Leetcode 4. Median of Two Sorted Arrays(中位数+二分答案+递归)

时间:2023-03-09 18:01:14
Leetcode 4. Median of Two Sorted Arrays(中位数+二分答案+递归)
4. Median of Two Sorted Arrays
Hard

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小的数,即这个相等的元素。

 class Solution {
public:
double findKth(vector<int>& nums1, int p1, vector<int>& nums2, int p2, int k){
int len1 = nums1.size(), len2 = nums2.size();
if(len1-p1>len2-p2){
return findKth(nums2,p2,nums1,p1,k);
}
if(len1==p1){
return nums2[p2+k-];
}
if(k==){
return min(nums1[p1],nums2[p2]);
}
int l1 = min(k/ + p1,len1), l2 = p2 + k - l1 + p1;;
int max1 = nums1[l1-], max2 = nums2[l2-];
if(max1==max2) return max1;
else if(max1<max2){
return findKth(nums1,l1,nums2,p2,k-l1+p1);
}
else {
return findKth(nums1,p1,nums2,l2,k-l2+p2);
}
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int len1 = nums1.size(),len2 = nums2.size();
if((len1+len2)%==){
return (findKth(nums1,,nums2,,(len1+len2)/)+findKth(nums1,,nums2,,(len1+len2+)/))/;
}
else{
return findKth(nums1,,nums2,,(len1+len2+)/);
}
}
};
#下面这个代码思路完全一样,加了一些注释,是来自别人的博客,附上原地址,感谢大佬:https://www.cnblogs.com/mini-coconut/p/9066508.html

 1 double findKth(vector<int> &nums1, int i, vector<int> &nums2, int j, int k)
{
// 首先需要让数组1的长度小于或等于数组2的长度
if (nums1.size() - i > nums2.size() - j) {
return findKth(nums2, j, nums1, i, k);
}
// 判断小的数组是否为空,为空的话,直接在另一个数组找第K个即可
if (nums1.size() == i) {
return nums2[j + k - ];
}
// 当K = 1时,表示我们要找第一个元素,只要比较两个数组的第一个元素,返回较小的那个即可
if (k == ) {
return min(nums1[i], nums2[j]);
}
int pa = min(i + k / , int(nums1.size())), pb = j + k - pa + i; if (nums1[pa - ] < nums2[pb - ]) {
return findKth(nums1, pa, nums2, j, k - pa + i);
}
else if (nums1[pa - ] > nums2[pb - ]) {
return findKth(nums1, i, nums2, pb, k - pb + j);
}
else {
return nums1[pa - ];
}
}
double findMedianSortedArrays(vector<int> A, vector<int> B) {
int sizeA = A.size(), sizeB = B.size();
if (sizeA <= && sizeB <= ) {
return ;
}
int total = sizeA + sizeB;
if (total % == ) {
return findKth(A, , B, , total / + );
}
else {
return (findKth(A, , B, , total / ) + findKth(A, , B, , total / + )) / ;
}
}

这里比较难理解的点是判断(nums1[pa - 1] < nums2[pb - 1])之后执行了return findKth(nums1, pa, nums2, j, k - pa + i);其实这个操作是因为目前nums1的分界线的值小于nums2分界线的值,那么证明nums1分界线以及前面的值都小于合并后的第k的值,也就是中位数。那么我们可以从这里开始,继续寻找第k-(pa-i)的值,直到两个值相等为止。