给出两个有序的数组,求其中位数,元素个数位偶数个,则求其上中位数和下中位数的平均数.
最简单粗暴的做法就是直接从两个数组的开头对比,建立两个指针遍历数组,时间为O(n+m).(数组1长度为n数组2长度为m)
如果想要log的时间复杂度就得使用二分了,如果是计算其中位数,序列中必然有(m+n)/2个数小于中位数mid,我们就根据这个公式就可以进行二分,我们先取数组1的中位数mid1,数组1中有n/2个数小于mid1,那么我们用它与数组2的第((m+n)/2-n/2+1)个数与其比较,这是就出现了两种情况,一种是大于,一种是小于,等于包含在哪边都可以.
其实这时候数组1和数组2都被分割成了两部分,下面也进入到了今天重点的位置.
如果大于则数组1的后半部分和数组2的前半部分就已经可能是中位数了,因为不符合中位数的公式.
同理如果小于,那么数组1的前半部分和数组2的后半部分就可以排除了.
这样我们就可以将其简化成了一个二分查找的情况.但是这道题有一定难度的地方就是整体序列为偶数个数的时候,我们需要求两个中位数将其相加并除2,但是如果这两个数每个数组占了一个,这就是一个难点.
这时候我们有两种思路,一种是先判断序列的元素个数,如果为奇数,直接求解中位数返回即可,如果为偶数那么就先求上中位数再求下中位数,将其相加除2再返回即可.
解法1:分别求上下中位数
class Solution {
public:
double findMedianSortedArrays(int A[], int m, int B[], int n) {
int mid_a = m/2, mid_b = n/2;
if(m == 0)
{
if(n % 2 == 0)
return (B[mid_b] + B[mid_b-1]) / 2.0;
else return B[mid_b];
}
else if(n == 0)
{
if(m % 2 == 0)
return (A[mid_a] + A[mid_a-1]) / 2.0;
else return A[mid_a];
}
if((m+n) % 2)
return helper_up(A, m, B, n);
else return (helper_up(A, m, B, n) + helper_down(A, m, B, n)) / 2.0;
}
int helper_up(int A[], int m, int B[], int n)
{
int mid_a = m/2, mid_b = n/2;
if(m == 1)
{
if(n == 1)
return A[0] < B[0] ? B[0] : A[0];
if(n % 2 == 0)
{
if(A[0] >= B[mid_b])
return B[mid_b];
else if(A[0] <= B[mid_b-1])
return B[mid_b-1];
else return A[0];
}
else
{
if(A[0] >= B[mid_b+1])
return B[mid_b+1];
else if(A[0] <= B[mid_b])
return B[mid_b];
else return A[0];
}
}
else if(n == 1)
{
if(m % 2 == 0)
{
if(B[0] >= A[mid_a])
return A[mid_a];
else if(B[0] <= A[mid_a-1])
return A[mid_a-1];
else return B[0];
}
else
{
if(B[0] >= A[mid_a+1])
return A[mid_a+1];
else if(B[0] <= A[mid_a])
return A[mid_a];
else return B[0];
}
}
else
{
int cutLen = mid_a > mid_b ? mid_b:mid_a;//注意每次减去短数组的一半,如果数组长度n是奇数,一半是指n-1/2
if(A[mid_a] == B[mid_b])
return A[mid_a];
else if(A[mid_a] < B[mid_b])
return helper_up(&A[cutLen], m - cutLen, B, n - cutLen);
else return helper_up(A, m - cutLen, &B[cutLen], n-cutLen);
}
}
int helper_down(int A[], int m, int B[], int n)
{
int mid_a = (m-1)/2, mid_b = (n-1)/2;
if(m == 1)
{
if(n == 1)
return A[0] < B[0] ? A[0] : B[0];
if(n % 2 == 0)
{
if(A[0] >= B[mid_b+1])
return B[mid_b+1];
else if(A[0] <= B[mid_b])
return B[mid_b];
else return A[0];
}
else
{
if(A[0] >= B[mid_b])
return B[mid_b];
else if(A[0] <= B[mid_b-1])
return B[mid_b-1];
else return A[0];
}
}
else if(n == 1)
{
if(m % 2 == 0)
{
if(B[0] >= A[mid_a+1])
return A[mid_a+1];
else if(B[0] <= A[mid_a])
return A[mid_a];
else return B[0];
}
else
{
if(B[0] >= A[mid_a])
return A[mid_a];
else if(B[0] <= A[mid_a-1])
return A[mid_a-1];
else return B[0];
}
}
else
{
int cutLen = (m/2 > n/2 ? n/2:m/2);//注意每次减去短数组的一半,如果数组长度n是奇数,一半是指n-1/2
if(A[mid_a] == B[mid_b])
return A[mid_a];
else if(A[mid_a] < B[mid_b])
return helper_down(&A[cutLen], m - cutLen, B, n - cutLen);
else return helper_down(A, m - cutLen, &B[cutLen], n-cutLen);
}
}
};
如果我们进一步抽象一下就可以得出一个更加通用的算法,那就是求解两个有序数组的第K大数,这样这个算法就更加通用,其实思想是一样的只不过我们上面的算法中的k是固定的.
class Solution:
# @return a float
def findMedianSortedArrays(self, A, B):
l=len(A)+len(B)
return self.findKth(A,B,l//2) if l%2==1 else (self.findKth(A,B,l//2-1)+self.findKth(A,B,l//2))/2.0
def findKth(self,A,B,k):
if len(A)>len(B):#确保A长度为最小的那一个,如果其中一个为空,就直接反悔另外一个的第K个数
A,B=B,A
if not A:
return B[k]
if k==len(A)+len(B)-1:#要求的是第K大的数,如果两个数组长度加起来为k,那么只需比较最后一个元素就可得出结果.
return max(A[-1],B[-1])
i=len(A)//2#取A数组的下中位数
j=k-i#取B的第k-i+1个数
if A[i]>B[j]:
return self.findKth(A[:i],B[j:],i)
#去掉不符合的两个部分,k变成i是因为整个序列最小的k-i个数已经被去掉了,k=k-(k-i)=i
else:
return self.findKth(A[i:],B[:j],j)
#去掉不符合的两个部分,k变成j是因为整个序列最小的i个数已经被去掉了,k=k-i
解法2:求解第K大数函数求解 C++
class Solution {
public:
double findMedianSortedArrays(int A[], int m, int B[], int n) {
int mid_a = m/2, mid_b = n/2;
if(m == 0)
{
if(n % 2 == 0)
return (B[mid_b] + B[mid_b-1]) / 2.0;
else return B[mid_b];
}
else if(n == 0)
{
if(m % 2 == 0)
return (A[mid_a] + A[mid_a-1]) / 2.0;
else return A[mid_a];
}
if((m+n) % 2)
return findKthSmallest(A, m, B, n, (m+n+1)/2);
else return (findKthSmallest(A, m, B, n, (m+n)/2) + findKthSmallest(A, m, B, n, (m+n)/2+1)) / 2.0;
}
//找到两个有序数组中第k小的数,k>=1
int findKthSmallest(int vec1[], int n1, int vec2[], int n2, int k)
{
//边界条件处理
if(n1 == 0)return vec2[k-1];
else if(n2 == 0)return vec1[k-1];
if(k == 1)return vec1[0] < vec2[0] ? vec1[0] : vec2[0];
int idx1 = n1*1.0 / (n1 + n2) * (k - 1);
int idx2 = k - idx1 - 2;
if(vec1[idx1] == vec2[idx2])
return vec1[idx1];
else if(vec1[idx1] < vec2[idx2])
return findKthSmallest(&vec1[idx1+1], n1-idx1-1, vec2, idx2+1, k-idx1-1);
else
return findKthSmallest(vec1, idx1+1, &vec2[idx2+1], n2-idx2-1, k-idx2-1);
}
};