4. 寻找两个正序数组的中位数 #
链接 #
题目 #
给定两个大小分别为 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(n) 的解法,但是简单容易记忆:
class Solution {
public:
double slice_mid(vector<int> v, int from, int to) {
auto size = (from - to + 1);
auto mid = from + (to - from) / 2; // 靠下的中点
if (size % 2 == 0) {
return (double(v[mid]) + double(v[mid + 1])) / 2.0;
} else {
return v[mid];
}
}
double findMedianSortedArrays(vector<int> &a, vector<int> &b) {
if (a.size() > b.size()) {
return findMedianSortedArrays(b, a);
}
int al = 0, ar = a.size() - 1;
int bl = 0, br = b.size() - 1;
while (true) {
auto size_a = ar + 1 - al;
auto size_b = br + 1 - bl;
// 如果两个都只剩一个,则返回平均值
if (size_a == 1 && size_b == 1) {
return (double(a[ar]) + double(b[br])) / 2;
}
// 如果某个数组空,则返回另一个的中位数
if (size_a == 0) {
return slice_mid(b, bl, br);
}
if (size_b == 0) {
return slice_mid(a, al, ar);
}
// 从小端删除左边
if (a[al] <= b[bl]) {
al++;
} else {
bl++;
}
// 从大端删除右边
if (a[ar] > b[br]) {
ar--;
} else {
br--;
}
}
}
};
另一个比较好的题解 #
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int tot = nums1.length + nums2.length;
if (tot % 2 == 0) {
int left = find(nums1, 0, nums2, 0, tot / 2);
int right = find(nums1, 0, nums2, 0, tot / 2 + 1);
return (left + right) / 2.0;
} else {
return find(nums1, 0, nums2, 0, tot / 2 + 1);
}
}
// i和j:分别表示当前考虑的n1和n2的起始索引。
// k:要找的第k小的元素。
int find(int[] nums1, int i, int[] nums2, int j, int k) {
if (nums1.length - i > nums2.length - j) return find(nums2, j, nums1, i, k);
if (i >= nums1.length) return nums2[j + k - 1];
if (k == 1) {
return Math.min(nums1[i], nums2[j]);
} else {
int si = Math.min(i + (k / 2), nums1.length); // nums1 左半段中点
int sj = j + k - (k / 2); // nums2 右半段中点
if (nums1[si - 1] > nums2[sj - 1]) { // 比较的是第一段的最后一个元素和第二段的前一个元素
return find(nums1, i, nums2, sj, k - (sj - j));
} else {
return find(nums1, si, nums2, j, k - (si - i));
}
}
}
}
关键:为什么可以舍弃部分数组
如果A数组中的第k/2大的数字小于B数组中的第k/2大的数字,这意味着A数组中最多只有k/2个数字可能是第k小的数字。因为B数组中有k/2个数字比A数组中的所有数字都小,所以A数组中较小的k/2个数字不可能是第k小的数字,可以舍弃。
反之亦然,如果B数组中的数字较小,则可以舍弃B数组中较小的k/2个数字。