leetcode:4. 寻找两个正序数组的中位数
题目
给定两个大小分别为 m
和 n
的正序(从小到大)数组 nums1
和 nums2
。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 **O(log (m+n))**
。
示例:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
解答 & 代码
题目要求时间复杂度应该为 **O(log (m+n))**
,因此只能用二分查找。
要寻找两个正序数组的中位数,
- 如果两个数组的总长度
len
为偶数,那么要找到第len/2
小的元素和第len/2+1
小的元素,求平均; - 如果两个数组的总长度
len
为奇数,那么要找到第len/2+1
小的元素。
因此问题转换为:寻找两个正序数组中第 k 小的元素
class Solution {
private:
// 寻找两个正序数组 nums1、nums2 中第 k 小的数,并返回
int getKthElement(vector<int>& nums1, vector<int>& nums2, int k)
{
int len1 = nums1.size(); // 数组 1 的长度
int len2 = nums2.size(); // 数组 2 的长度
int left1 = 0; // nums1 数组中有效元素的左边界,left1 左边的元素都不可能是第 k 小的元素
int left2 = 0; // nums2 数组中有效元素的左边界,left2 左边的元素都不可能是第 k 小的元素
while(true)
{
/* 边界情况 */
// 边界情况 1:如果一个数组的有效部分为空,直接返回另一个数组中第 k 小的元素
if(left1 == len1)
return nums2[left2 + k - 1];
if(left2 == len2)
return nums1[left1 + k - 1];
// 边界情况 2:如果 k == 1,返回两个数组首元素的最小值
if(k == 1)
return min(nums1[left1], nums2[left2]);
/* 正常情况 */
// 分别取两个数组有效部分第 k/2 小的元素下标 idx1、idx2
// 如果idx1 or idx2 越界,则取对应数组最后一个元素下标
int idx1 = min(len1 - 1, left1 + k / 2 - 1);
int idx2 = min(len2 - 1, left2 + k / 2 - 1);
// 如果 nums1[idx1] <= nums2[idx2],那么 idx1 前面最多有 2*(k/2 - 1) 个元素比它小
// 即 nums1[idx1] 最多只能是第 k-1 小的元素,因此 nums1[0..idx1] 部分都是无效的,删除
if(nums1[idx1] < nums2[idx2])
{
k -= idx1 - left1 + 1; // 更新 k 值,减去 nums1 删除/无效的元素个数
left1 = idx1 + 1; // 更新 left1 范围
}
// nums2[0..idx2] 的元素都不可能是第 k 小的元素,全部删除,缩小范围
else
{
k -= idx2 - left2 + 1; // 更新 k 值,减去 nums2 删除/无效的元素个数
left2 = idx2 + 1; // 更新 left2 范围
}
}
}
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int len = nums1.size() + nums2.size(); // 两个数组总长度
// 如果两个数组总长度 len 为偶数
// 需要找到第 len/2 小的元素和第 len/2+1 小的元素,求平均
if(len % 2 == 0)
{
int mid1 = getKthElement(nums1, nums2, len / 2);
int mid2 = getKthElement(nums1, nums2, len / 2 + 1);
return (mid1 + mid2) / 2.0;
}
// 如果两个数组总长度 len 为奇数:需要找到第 len/2+1 小的元素
else
return getKthElement(nums1, nums2, len / 2 + 1);
}
};
复杂度分析:设两个数组长度分别为 M、N
- 时间复杂度 O(log(M + N)):初始时 K = (M + N) / 2 or K = (M + N) / 2 + 1。每轮循环可以将查找范围减少 K/2 个元素(每轮循环 K 也会减小 2/K,即删除的无效元素个数),因此时间复杂度 O(log K) = O(log (M + N))
- 空间复杂度 O(1)
执行结果:
执行结果:通过
执行用时:24 ms, 在所有 C++ 提交中击败了 78.36% 的用户
内存消耗:86.8 MB, 在所有 C++ 提交中击败了 88.28% 的用户