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 小的元素elsereturn 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% 的用户
