给定两个大小为 m 和 n 的正序(从小到大)数组 nums1
和 nums2
。
请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1
和 nums2
不会同时为空。
示例 1:
nums1 = [1, 3] nums2 = [2] 则中位数是 2.0
示例 2:
nums1 = [1, 2] nums2 = [3, 4] 则中位数是 (2 + 3)/2 = 2.5
首先想到的算法是一个i扫描nums1, 一个j扫描nums2, 两面总共扫描(m + n)/ 2次就好
但那个算法复杂度是 O(m + n)的
再看题目要求是O(log(m + n)), 一定是二分递规
这里我们定义一个递归函数来求两个数组的第k个元素
如果k = 1, 那么肯定是两个数组的第一个数中的较小的那个
其他情况呢
两个数组都找第 k / 2 个元素
如果某一个数组长度小于 k / 2 , 那第k个元素肯定在另一个数组中
比较两个数组的第 k / 2 个元素,较小的那个的数组前 k / 2个元素中是肯定找不到了
然后在他剩下的部分和另一个数组中找第k - k / 2 个元素
这样就二分递归找到了两个数组的第k个数
还有一个问题,就是奇数个元素的中位数和偶数个元素的中位数的不同
这里偷个懒
不管奇数偶数个元素,中位数都等于(第(m + n + 1)/ 2个元素 + 第(m + n + 2)/ 2个元素 ) / 2
#include <iostream>
#include <vector>
#include <float.h>
using namespace std;
double findk(vector<int>& nums1, int i, vector<int>& nums2, int j, int k){
if(i >= nums1.size()){
return nums2[j + k - 1];
}
if(j >= nums2.size()){
return nums1[i + k - 1];
}
if(k == 1){
if(nums1[i] < nums2[j]){
return nums1[i];
}
else{
return nums2[j];
}
}
int mid1 = INT_MAX;
int mid2 = INT_MAX;
if(i + k/2 - 1 < nums1.size()){
mid1 = nums1[i + k/2 - 1];
}
if(j + k/2 - 1 < nums2.size()){
mid2 = nums2[j + k/2 - 1];
}
if(mid1 < mid2){
return findk(nums1, i + k / 2, nums2, j, k - k / 2);
}
else{
return findk(nums1, i, nums2, j + k / 2, k - k / 2);
}
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
int l = (m + n + 1) / 2;
int r = (m + n + 2) / 2;
return (findk(nums1, 0, nums2, 0, l) + findk(nums1, 0, nums2, 0, r)) / 2;
}
int main(){
int n1[] = {1, 3};
int n2[] = {2};
vector<int> nums1(n1, n1 + 2);
vector<int> nums2(n2, n2 + 1);
double re = findMedianSortedArrays(nums1, nums2);
cout << re << endl;
return 0;
}