给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
提示:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106
就题目本意来说,这题难道应该归类为简单题。但当要求时间复杂度为 O(log (m+n)) 时,level大大提高,解法有很多,但符合时间复杂度却很少。例如
方法一:合并数组,然后找。
方法二:直接在2个数组遍历,找到第k个最小的数
以上的时间复杂度都为O(m+n),不符合要求。
方法三:
这个问题等价于在2个数组中找出第k个小的数,推导过程如下:
当在2个数组A和B当中找第K小的数时,不妨先看
a:A[k/2-1] 和b:B[k/2-1]的大小。
如果a<=b,则在b数组中最多只有B[0]~B[k/2-2]共 k/2-1个小于a,又因为a数组是递增,所以至多有A[0] ~A[k/2-2]和B[0] ~ B[k/2-2]共计k-2个小于a,所以a必然不是第k小的数,则可以排除A[0] ~A[k/2-1]这些范围,k=k-k/2;
要注意以下几种特殊情况:
初始:
k=(4+9+1)/2=7
A:1 3 4 9
B:1 2 3 4 5 6 7 8 9
第一次:
K/2-1=2比较:A[2]>B[2]
数组筛选后为:
A:1 3
B:4 5 6 7 8 9
k=k-k/2=4;
第二次:
K/2-1=1 比较:3和5
数组筛选后为:
A:4 9
B:4 5 6 7 8 9
k=K-k/2=2
第三次:
k/2-1=0 比较 4 4
数组筛选后为:
A:9
B:4 5 6 7 8 9
k=K-k/2=1
第四次
k为1时,选取2个数组最小的数字,为4
解法四:
题解写的严谨易懂,这里就概括一下,省去一些证明。
将A B分别进行某种划分和合并
left_part | right_part
A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]
B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
中位数就是前一部分的最大值和后一部分的最小值的平均值:
median=(max(left_part)+min(right_part))/2
中位数就是前一部分的最大值:
median=(max(left_part))
我们注意到当m+n为偶数时,i+j=(m+n)/2 和i+j=(m+n+1)/2是相等的,于是i=(m+n+1)/2-j这定了划分时左右数目关系,B[j−1]≤A[i] 且 A[i−1]≤B[j] 定义了左边最大数字<=右边最小数
于是数学语言描述如下
在 [0, m]中找到 i,使得:
B[j−1]≤A[i] 且 A[i−1]≤B[j],其中i=(m+n+1)/2-j;
等价于:
在 [0, m]中找到最大的 i,使得:
A[i−1]≤B[j],其中i=(m+n+1)/2-j;
于是解法为:i 在 [0, m]的区间上进行二分搜索,找到最大的i满足,A[i−1]≤B[j],其中i=(m+n+1)/2-j
这里要注意一下几点:
根据中位数的定义,当 m+n 是奇数时,中位数是两个有序数组中的第 (m+n)/2 个元素,当 m+n 是偶数时,中位数是两个有序数组中的第 (m+n)/2个元素和第 (m+n)/2+1 个元素的平均值。因此,这道题可以转化成寻找两个有序数组中的第 k小的数,其中 k 为 (m+n)/2 或 (m+n)/2+1
这里如果是奇数时,如果这里的中位数是第 (m+n)/2 个元素,那只能理解为从0开始计数,这和下面的偶数矛盾了。这里应该改为 (m+n+1)/2个元素,后面题解对应的代码也是(m+n+1)/2
思路三解法:个人看到二分习惯性用递归了,就一直想怎么用递归写。最后完成了,但感觉太累赘了,耗时也很多(用了vector容器的增删改,这里应该是超复杂度了)后来想想应该直接用数组下标表示的,虽然麻烦但耗时少,唉,小菜鸡一个。
class Solution {
public:
double fun(vector<int> a, vector<int> b,int k){
//递归边界2
if(a.size()==0){
return b[k-1];
}
else if(b.size()==0){
return a[k-1];
}
//递归边界1
else if(k==1){
return a[0]>b[0]? b[0]:a[0];
}
else{
//数组越界情况
if ((k/2-1)>a.size()-1){
if(a.back()<=b[k/2-1])
{k=k-a.size();
a.clear();
return fun(a,b,k);}
else{
b.erase(b.begin(),b.begin()+k/2);
k=k-k/2;
return fun(a,b,k);
}
}
// 数组越界情况
else if ((k/2-1)>b.size()-1){
if(b.back()<=a[k/2-1])
{k=k-b.size();
b.clear();
return fun(a,b,k);}
else{
a.erase(a.begin(),a.begin()+k/2);
k=k-k/2;
return fun(a,b,k);
}
}
//正常比较
else if(a[k/2-1]<=b[k/2-1]){
a.erase(a.begin(),a.begin()+k/2);
k=k-k/2;
return fun(a,b,k);
}
else{
b.erase(b.begin(),b.begin()+k/2);
k=k-k/2;
return fun(a,b,k);
}
}
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int k=nums1.size()+nums2.size();
if(k%2==0){
return (fun(nums1, nums2, k/2)+fun(nums1, nums2, k/2+1))/2.0;
}
else return fun(nums1,nums2,(k+1)/2);
}
};
下面附上官方标解:
class Solution {
public:
int getKthElement(const vector<int>& nums1, const vector<int>& nums2, int k) {
int m = nums1.size();
int n = nums2.size();
int index1 = 0, index2 = 0;
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]);
}
// 正常情况
int newIndex1 = min(index1 + k / 2 - 1, m - 1);
int newIndex2 = min(index2 + k / 2 - 1, n - 1);
int pivot1 = nums1[newIndex1];
int pivot2 = nums2[newIndex2];
if (pivot1 <= pivot2) {
k -= newIndex1 - index1 + 1;
index1 = newIndex1 + 1;
}
else {
k -= newIndex2 - index2 + 1;
index2 = newIndex2 + 1;
}
}
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int totalLength = nums1.size() + nums2.size();
if (totalLength % 2 == 1) {
return getKthElement(nums1, nums2, (totalLength + 1) / 2);
}
else {
return (getKthElement(nums1, nums2, totalLength / 2) + getKthElement(nums1, nums2, totalLength / 2 + 1)) / 2.0;
}
}
};
感受:没有对比就没有伤害
解法四代码:
class Solution {
public:
double findMedianSortedArrays(vector<int>& a, vector<int>& b) {
if(a.size()>b.size())
return findMedianSortedArrays(b, a);
int m=a.size();
int n=b.size();
int left=0,right=m;
//考虑一下四个特殊情况
int meidian1=0,meidian2=0;
while(left<=right){
int i=(left+right)/2;
int j=(m+n+1)/2-i;
//要求b[j−1]≤a[i] 且 a[i−1]≤b[j]
//下面对 i=0时的a[i-1] i=m时的a[m] j=0时的b[j-1] j=n时的b[n]进行特殊处理
int left_max_i=(i==0?INT_MIN :a[i-1]);
int right_min_i=(i==m?INT_MAX :a[i]);
int left_max_j=(j==0?INT_MIN :b[j-1]);
int right_min_j=(j==n?INT_MAX :b[j]);
//下面就是二分查找到那个最大的i,且满足left_max_i <= ritht_min_j
if(left_max_i <= right_min_j){
meidian1=max(left_max_i,left_max_j);
meidian2=min(right_min_i,right_min_j);
left=i+1;
}
else{
right=i-1;
}
}
if((m+n)%2==0){
return (meidian1+meidian2)/2.0;
}
else{
return meidian1;
}
}
};