04. 寻找两个正序数组的中位数

本文最后更新于:25 天前

04. 寻找两个正序数组的中位数

题目描述

给定两个大小分别为 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

题解

解法一:分别从两个数组中按照升序不断取元素,直到取到 m+n/2 个元素,然后取中位数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size(), m = nums2.size();
int total = n + m;
int target1 = (total + 1) / 2, target2 = (total + 2) / 2;
int i = 0, j = 0, cnt = 0;
double median1 = 0, median2 = 0;

while (i < n || j < m) {
int num;
if (i < n && (j >= m || nums1[i] < nums2[j])) {
num = nums1[i++];
} else {
num = nums2[j++];
}
cnt++;
if (cnt == target1) {
median1 = num;
}
if (cnt == target2) {
median2 = num;
break;
}
}

return (median1 + median2) / 2;
}
};

解法二:归并排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
/*
解题思路:
合并两数组,并利用std::sort进行排序,
对合并后的有序数组进行中位数求解,分两种情况:
1.数组元素个数为奇数,则直接取下标为【(n+1)/2】的元素为结果;
2.数组元素个数为偶数,则取下标为【len/2-1】和【len/2】元素的平均值作为结果
*/
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
double midNum = 0.0;
for (int i=0; i<nums2.size(); i++){
nums1.push_back(nums2[i]);
}
sort(nums1.begin(), nums1.end());
int len = nums1.size();
if (0 == len%2){
len = len/2;
midNum = (nums1[len-1] + nums1[len]) / 2.0;
}
else{
midNum = nums1[len/2]/1.0;
}
return midNum;
}
};

解法三:二分查找 (分治)

解法一、二的时间复杂度均为 O(m+n),尽管能 AC,但是并不满足题目要求。题目要求时间复杂度为 O(log(m+n)),因此需要使用二分查找进行降低时间复杂度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size(), n = nums2.size();
function<int(int, int, int)> f = [&](int i, int j, int k) {
if (i >= m) {
return nums2[j + k - 1];
}
if (j >= n) {
return nums1[i + k - 1];
}
if (k == 1) {
return min(nums1[i], nums2[j]);
}
int p = k / 2;
int x = i + p - 1 < m ? nums1[i + p - 1] : 1 << 30;
int y = j + p - 1 < n ? nums2[j + p - 1] : 1 << 30;
return x < y ? f(i + p, j, k - p) : f(i, j + p, k - p);
};
int a = f(0, 0, (m + n + 1) / 2);
int b = f(0, 0, (m + n + 2) / 2);
return (a + b) / 2.0;
}
};

思路:两个升序数组求中位数,简化问题为求第 (m+n+1)/2 和第 (m+n+2)/2 个数的平均值,即找到两个正序序列的第 (m+n+1)/2 小的数和第 (m+n+2)/2 小的数。此时问题可归纳为:两个正序序列,如何找到第 k 小的数。二分法查找,每个数组中各取 k/2 的元素比较大小,较小部分的元素必然不是第 k 小的数,因此移动较小部分的指针,递归继续。

总结

  1. 第一直觉:归并排序,解法一和二本质相同,但时间复杂度无法满足题解要求
  2. 关键点:O(log(m+n)),二分查找
  3. 简化问题模型,根据中位数特性,将问题转化为求第 k 小的数,二分查找迭代计算
  4. 解法三使用lambda表达式,以及函数递归调用,会导致耗时和内存消耗增加,提交测试耗时和内存同解法一相似,可简化为普通函数和循环计算,避免递归次数过多导致栈溢出

04. 寻找两个正序数组的中位数
https://ccccx159.github.io/2024/04/13/leetcode/04.寻找两个正序数组的中位数/
作者
Xu@n Ch3n
发布于
2024年4月13日
更新于
2024年4月18日
许可协议