LeetCode/寻找两个正序数组中位数
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数
1. 辅助空间暴力法
将两数组合并,分奇偶取中位数
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
vector<int> merge;
int i =0; int j=0;
while(i<m&&j<n){
if(nums1[i]<=nums2[j]){ merge.push_back(nums1[i]); i++;}
else {merge.push_back(nums2[j]); j++;}
}
while(i<m){merge.push_back(nums1[i]); i++;}
while(j<n){merge.push_back(nums2[j]); j++;}
if((m+n)%2==0) return (merge[(m+n)/2-1]+ merge[(m+n)/2])/2.0;
else return merge[(m+n)/2];
}
};
2. 双指针暴力法
不需要辅助空间,让两指针边比较边移动,到中间位置即可,分奇偶输出结果
这里不要单纯记录指针,单纯记录指针的话,最后获得结果的时候,既要讨论是否为空集
又要讨论两指针哪个数为后一个数,以及求在偶数的情况下前一个数的位置,非常麻烦
干脆每次遍历都记录更新两个对应值,最后直接得到中位数
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
int len = m+n;
int left =-1; int right=-1;//记录中位数值,避免单纯记录指针的分类讨论,避免处理一数组为空集
int start1 = 0; int start2 = 0;//两指针初始位置
for(int i=0;i<=len/2;i++){//直接遍历len/2 + 1次 ,不用指针进行跳出,避免分类讨论
left = right;
//处理边界条件和移动条件
if(start1 < m && (start2 >= n || nums1[start1] < nums2[start2]))
{right = nums1[start1]; start1++;}
else {right = nums2[start2]; start2++;}
}
if(len%2==0) return (left + right) / 2.0;
else return right;
}
};
3. 二分法
因为已经是有序的,合理使用二分性能最好
关键在于如何通过二分使问题规模变小,容易判断通过取两数组中间位置进行比较
可以排除掉较小数前面所有数,但对剩下的数来说问题发生了改变,不再是求中位数
其实求中位数本质上是求第len/2小的数,所以可以写求第k小的数
在排除一部分数后,求取的k也随之改变,注意递归的边界条件
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
int left = (m+n+1)/2;
int right = (m+n+2)/2;
return (getKth(nums1,0,m-1,nums2,0,n-1,left)+getKth(nums1,0,m-1,nums2,0,n-1,right))*0.5;
}
int getKth(vector<int>& nums1,int start1,int end1,
vector<int>& nums2,int start2,int end2,int k){
int len1 = end1 - start1 + 1;
int len2 = end2 - start2 + 1;
//处理边界
if (len2 == 0) return nums1[start1 + k - 1];//空集的情况
if (len1 == 0) return nums2[start2 + k - 1];//空集的情况
if (k == 1) return min(nums1[start1], nums2[start2]);//k规模缩减至1
int i = start1 + min(len1, k / 2) - 1;//取中间值或边界
int j = start2 + min(len2, k / 2) - 1;//取中间值或边界
if (nums1[i] > nums2[j])//
//缩小问题规模,数组二从j+1开始,其前面的数必然不是第k小,同时k发生改变
return getKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));
//否则从数组一i+1开始,k发生改变
else return getKth(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1));
}
};