题目参见寻找两个正序数组的中位数。思路来自LeetCode的官方题解。
寻找两个正序数组的中位数 给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和nums2。请你找出并返回这两个正序数组的中位数。如果是奇数,则返回第(m+n+1)/2小的数字,如果是偶数,则返回第(m+n)/2小的数和第(m+n)/2+1小的数两数平均值。
方案一:归并。
方案1.1 真归并
由于题目中的两个数组都是有序数组,所以可以开辟一个空间大小为 m + n m+n m+n的数组,用以保存归并后的新有序数组。每次从两个数组之中选择一个较小的数字放到归并后的新数组中,最后从新数组里面挑选题目要求的中位数,并返回即可。
方案1.2 伪归并
事实上,我们并不需要真正的开辟一个空间大小为 m + n m+n m+n这么大的新数组,我们其实只需要第(m+n+1)/2大的这一个数字(如果是偶数,则需要两个)数字而已,何必要开一个完整数组用以保存呢?所以,维护一个数字,在两个数组间进行两两比较的时候,只存留两个数组间最小的那个数字,一直递增下标,直到找到第(m+n+1)/2小的数字为止即可。这样空间复杂度则降为了 O ( 1 ) O(1) O(1)级别。代码如下:
class Solution {
public:
double findMedianSortedArrays(vector<int>& a, vector<int>& b) {
int la = a.size();
int lb = b.size();
int i = 0, j = 0, id = 0, ans = 0, n = la + lb;
if(n==1){
for(auto t:a)return t;
for(auto t:b)return t;
}
if(n%2){
do{
if(i<la&&j<lb){
if(a[i]<b[j]) ans = a[i++];
else ans = b[j++];
}
else if(i < la) ans = a[i++];
else if(j < lb) ans = b[j++];
id++;
}while(id!=(n+1)/2);
return ans;
}
else{
double sum = 0;
do{
if(i<la&&j<lb){
if(a[i]<b[j]) ans = a[i++];
else ans = b[j++];
}
else if(i < la) ans = a[i++];
else if(j < lb) ans = b[j++];
id++;
if(id==n/2||id==n/2+1){
sum += ans;
}
}while(id!=n/2+1);
return sum / 2;
}
}
};
方案二:二分查找
更普遍而言,我们考虑寻找两个有序数组中的第K小的数字。既然是两个数组,第一个数组中的第 K 2 \frac{K}{2} 2K个数字必然大于第一个数组中前面第 K 2 − 1 \frac{K}{2}-1 2K−1个数字,第二个数组中的第 K 2 \frac{K}{2} 2K个数字必然大于第二个数组中的前面第 K 2 − 1 \frac{K}{2}-1 2K−1个数字;这二者中最小的数字,最多也只能是大于了前面第 K 2 − 1 + K 2 − 1 = K − 2 \frac{K}{2}-1+\frac{K}{2}-1=K-2 2K−1+2K−1=K−2个数字(为什么是“最多”呢,因为很有可能这个数字比另一组数字中的前 K 2 − 1 \frac{K}{2}-1 2K−1个数字都要小),不会是第 K K K小的数字了,需要从该数字后面进行下一次二分查找,同时也排除掉了 K 2 \frac{K}{2} 2K个数字(如果该数组中容量不足 K 2 \frac{K}{2} 2K个,则会减少掉该数组的总容量,我们记排除掉的数字为 t t t个),下次寻找的应该是第 K − t K-t K−t小的数字了。思路清楚后,编写代码即可。
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
if ((m + n) %2 == 1){
return getTopK((m+n)/2+1,m,n,nums1,nums2);
}else{
return (getTopK((m+n)/2,m,n,nums1,nums2)+getTopK((m+n)/2+1,m,n,nums1,nums2))/2.0;
}
}
int getTopK(int k, int m, int n, vector<int>& nums1, vector<int>& nums2){
int index1 = 0, index2 = 0;
int mid_index1,nums_index1;
int mid_index2,nums_index2;
while(true){
if(index1 == m){
return nums2[index2 + k - 1];
}
if(index2 == n){
return nums1[index1 + k -1];
}
if(k == 1){
return min(nums1[index1],nums2[index2]);
}
mid_index1 = min(index1 + k / 2 - 1 , m-1);
mid_index2 = min(index2 + k / 2 -1 ,n-1);
nums_index1 = nums1[mid_index1];
nums_index2 = nums2[mid_index2];
if (nums_index1 < nums_index2){
k -= mid_index1 - index1 + 1;
index1 = mid_index1 +1;
}else{
k -= mid_index2 - index2 + 1;
index2 = mid_index2 + 1;
}
}
}
};
本文介绍了一种寻找两个有序数组中位数的方法,包括归并和二分查找两种策略。归并策略分为真归并与伪归并,后者通过优化空间复杂度提升效率。二分查找方案则更加高效地定位目标值。
179

被折叠的 条评论
为什么被折叠?



