题目:
There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
例子:
Example 1:
nums1 = [1, 3]
nums2 = [2]
The median is 2.0
Example 2:
nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5
问题解析:
给定两个排序的数组,给出两个数组的中位数。
链接:
思路标签
双指针、割和二分法、两个排序数组的第k个元素
解答:
- 问题为寻找中位数,这类问题属于寻找两个排序数组中的第k个元素。
- 下面给出双指针合并和割的第k个元素。
1. 双指针合并数组求中位数
- 利用两个指针分别指向第一和第二个数组;
- 利用两个数组的大小,使用计数直接找到对应的中位数。
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
int length = m + n;
int midl = 0, midr = 0;
if(length % 2 == 0){
midl = length/2;
midr = length/2+1;
}
else{
midl = length/2+1;
midr = length/2+1;
}
int k = 0;
int i = 0, j = 0;
int mid[2] = {0,0};
while(k < midr){
int a = (i < m)? nums1[i] : INT_MAX;
int b = (j < n)? nums2[j] : INT_MAX;
if(a <= b){
k++;
i++;
}else{
k++;
j++;
}
if(k == midl)
mid[0] = min(a, b);
if(k == midr)
mid[1] = min(a, b);
}
return (mid[0] + mid[1])/2.0;
}
};
2. 利用割的思想,以及二分法来寻找中位数。
- 利用割的思想寻找两个数组中的第k个元素:我们设:
- Ci为第i个数组的割;
- Li为第i个数组割后的左元素;
- Ri为第i个数组割后的右元素。
- 对于两个数组:
- 首先Li<=Ri是一定的(因为数组有序);
- 如果我们令L1<=R2 && L2<=R1,那么左半边 全小于右半边;
- 如果左边的元素个数相加刚好等于k,那么第k个元素就是Max(L1,L2);
- 如果 L1>R2,说明数组1的左边元素太大,我们把C1减小,把C2增大。
- L2>R1同理,把C1增大,C2减小。
- 例子:
- 假设k=3
- 对于 [1 4 7 9] [2 3 5]
- 设C1 = 2,那么C2 = k-C1 = 1
- [1 4/7 9] [2/3 5]
- 这时候,L1(4)>R2(3),说明C1要减小,C2要增大,C1 = 1,C2=k-C1 = 2
- [1/4 7 9] [2 3/5]
- 这时候,满足了L1<=R2 && L2<=R1,第3个元素就是Max(1,3) = 3。
- 中位数的特殊处理:(考虑奇偶的问题)
- 虚拟加入‘#’(这个trick在manacher算法中也有应用),让数组长度恒为奇数(2n+1恒为奇数)。
- 加入前:[1 4 7 9],长度4;加入后:[# 1 # 4 # 7 # 9 #],长度9;
- 加入前:[2 3 5],长度3;加入后:[# 2 # 3 # 5 #],长度7。
- 加完之后,每个位置可以通过/2得到原来元素的位置。
- 无论是奇数还是偶数的情况都有:
- Li = (Ci-1)/2, Ri = Ci/2
- 把2个数组看做一个虚拟的数组A,目前有2m+2n+2个元素,找中位数,割在m+n+1处,所以我们只需找到m+n+1位置的元素和m+n+2位置的元素就行了。
- 左边:A[m+n+1] = Max(L1+L2)
- 右边:A[m+n+2] = Min(R1+R2)
Mid = (A[m+n+1]+A[m+n+2])/2 = (Max(L1+L2) + Min(R1+R2) )/2
- 更快地寻找割的位置的方法:二分法
- 只要C1或C2确定,另外一个也就确定了。这里,为了效率,我们肯定是选长度较短的做二分,假设为C1。
- L1>R2,把C1减小,C2增大。—> C1向左二分
- L2>R1,把C1增大,C2减小。—> C1向右二分
- 如果C1或C2已经到头怎么办?这种情况出现在:如果有个数组完全小于或大于中值。可能有4种情况:
- C1 = 0 :数组1整体都比中值大,L1赋小值取L2,中值在2中;
- C1 = 2*m :数组1整体都比中值小,R1赋大值取R2,中值在2中;
- C2 = 0 :数组2整体都比中值大,L2赋小值取L1,中值在1中;
- C2 = 2*n:数组2整体逗比中值小,R2赋大值取R1,中值在1中。
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
if(m > n)
return findMedianSortedArrays(nums2, nums1);
int L1, R1, L2, R2, C1, C2, low = 0, high = 2*m;
while(low <= high){
C1 = (low + high)/2;
C2 = m+n - C1;
L1 = (C1 == 0)? INT_MIN : nums1[(C1-1)/2];
R1 = (C1 == 2*m)? INT_MAX : nums1[C1/2];
L2 = (C2 == 0)? INT_MIN : nums2[(C2-1)/2];
R2 = (C2 == 2*n)? INT_MAX : nums2[C2/2];
if(L1 > R2)
high = C1-1;
else if(L2 > R1)
low = C1+1;
else
break;
}
return (max(L1, L2) + min(R1, R2))/2.0;
}
};
- 解释引用自下面的博客,内有图文解释,更加详尽:https://blog.csdn.net/hk2291976/article/details/51107778