题意
两个有序数组,求它们的中位数。
思路
其实我们可以将这个问题转化为:求两个有序数组的第k大。
算法1
非递归版。
首先,我们假设在第一个数组里面任意找一个元素 ai ,那么它在第一个数组里是 i+1 大,如果我们知道它在第二个数组内是j大,那么在两个数组合成以后,它就是 i+j+1 大了。那么我们如何判断它在第二个数组内是第几大呢?因为第二个数组是有序的,所以我们只需要二分查找一下它的位置就好了。我们会存在如下问题:
使用
lower_bound
还是upper_bound
?然后,我们在二分查找的时候,其实会面临一个问题就是,比如我们的数组是[1, 2], [1, 2]。求第3大的时候,假如第一个数组内我们选择 a1=2 ,那么如果用
lower_bound
查找, j=1 ,这时候 i+j+1=k=3 ,是成立的。然后我们还需要找到第2大,第一个数组内我们选择 a1=2 ,那么用
lower_bound
查找的时候,这时候j = 1
。会使 i+j+1=3≠2 。假如我们用
upper_bound
查找呢?当 k=3 , a1=2 , j=2 , i+j+1=4≠k 。
也就是,我们存在一些重复元素的情况下,其实插入中间的任意位置都可以。
所以,我们的判断满足条件为:
int j1 = lower_bound(b.begin(), b.end(), a[i]) - b; int j2 = upper_bound(b.begin(), b.end(), a[i]) - b; if (i + j1 + 1 <= k && i + j2 + 1 >= k) return a[i];
如果结果不在第一个数组中呢?
我们只需要分别在第一个数组中和第二个数组中分别找一下就好。
如果m + n为偶数,我们需要找到两个数,怎么处理?
- 调用两次找第k大。
- 我们记录一下找到的第k大的位置pos,那么第k - 1大,要么为
nums1[pos - 1]
,要么为nums2[k - 1- pos]
。即两个数取较大的一个即可。
算法2
递归版。
在我们找第k大的数时,假设我们另 i=k2,j=k2 。我们比较一下 ai 和 bj 的值。
若
ai−1<bj−1
:说明第k大一定不在
[a0,ai−1]
内。于是,我们下一次的递归为find(a + i, m - i, b, n, k - i)
。
若
bj−1<ai−1
:说明第k大一定不在
[b0,bj−1]
内。于是,我们下一次的递归为find(a, m, b + j, n - j, k - j)
若 ai−1=bj−1 :说明第k大即为 ai−1 或 bj−1 。
边界条件:
- 若a已经为空了, 那么直接返回
b[k - 1]
即可。 - 若
k == 1
,那么,我们返回a[0]和b[0]中最小的一个即可。 - 我们确保m < n来满足第一个边界。
- i=k2 的时候,我们需要取 min{k2,m} 。
代码
//algorithm 1.1
class Solution {
public:
double kthNum(vector<int>& a, vector<int>& b, int k, int flag) {
int l = 0, r = k - 1, m;
while (l <= r) {
m = l + (r - l >> 1);
if (m >= a.size()) {r = m - 1; continue;}
int y1 = lower_bound(b.begin(), b.end(), a[m]) - b.begin();
int y2 = upper_bound(b.begin(), b.end(), a[m]) - b.begin();
if ((m + y1 + 1 <= k) && (m + y2 + 1 >= k)) return (double)a[m];
if (m + y1 + 1 > k) r = m - 1;
else l = m + 1;
}
//not in the first array
return 1e15;
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size(), n = nums2.size();
int k1 = m + n >> 1;
++k1;
double x1 = kthNum(nums1, nums2, k1, 1);
if (x1 == 1e15) x1 = kthNum(nums2, nums1, k1, 1);
if ((m + n) & 1) return x1;
--k1;
double x2 = kthNum(nums1, nums2, k1, 0);
if (x2 == 1e15) x2 = kthNum(nums2, nums1, k1, 0);
return (x1 + x2) / 2.0;
}
};
//algorithm 1.2
class Solution {
public:
int pos = 0;
double kthNum(vector<int>& a, vector<int>& b, int k) {
int l = 0, r = k - 1, m;
while (l <= r) {
m = l + (r - l >> 1);
if (m >= a.size()) {r = m - 1; continue;}
int y1 = lower_bound(b.begin(), b.end(), a[m]) - b.begin();
int y2 = upper_bound(b.begin(), b.end(), a[m]) - b.begin();
if ((m + y1 + 1 <= k) && (m + y2 + 1 >= k)) {pos = m; return (double)a[m];}
if (m + y1 + 1 > k) r = m - 1;
else l = m + 1;
}
//not in the first array
return 1e15;
}
double another(vector<int>& nums1, vector<int>& nums2, int k1) {
int y = --k1 - pos - 1; double x2;
if (pos) {
if (y >= 0) x2 = max(nums2[pos -1], nums1[y]);
else x2 = nums2[pos - 1];
} else {
x2 = nums1[y];
}
return x2;
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size(), n = nums2.size();
int k1 = m + n >> 1;
++k1; bool flag = false;
double x1 = kthNum(nums1, nums2, k1), x2;
if (x1 == 1e15) {flag = true; x1 = kthNum(nums2, nums1, k1);}
if ((m + n) & 1) return x1;
if (flag) x2 = another(nums1, nums2, k1);
else x2 = another(nums2, nums1, k1);
return (x1 + x2) / 2.0;
}
};
//algorithm 2
class Solution {
public:
double findKth(int a[], int m, int b[], int n, int k) {
if (m > n) return findKth(b, n, a, m, k);
if (m == 0) return b[k - 1];
if (k == 1) return min(a[0], b[0]);
int x = min(m, k >> 1), y = k - x;
if (a[x - 1] < b[y - 1]) return findKth(a + x, m - x, b, n, k - x);
if (a[x - 1] > b[y - 1]) return findKth(a, m, b + y, n - y, k - y);
return a[x - 1];
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size(), n = nums2.size();
int *a = new int[nums1.size()], *b = new int[nums2.size()];
for (int i = 0; i < m; i++) a[i] = nums1[i];
for (int i = 0; i < n; i++) b[i] = nums2[i];
if ((m + n) & 1) return findKth(a, m, b, n, (m + n >> 1) + 1);
return (findKth(a, m, b, n, (m + n >> 1) + 1) + findKth(a, m, b, n, m + n >> 1)) / 2.0;
}
};