LeetCode 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左半部的最大值、nums1右半部的最小值、nums2左半部的最大值、nums2右半部的最小值)来调整查找方向,最终找到中位数。详细步骤请看代码注释。
class Solution {
public:double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {// 如果nums1比nums2长,交换参数顺序递归调用// 这样可以确保nums1始终是较短的数组,减少二分查找的范围if (nums1.size() > nums2.size()) {return findMedianSortedArrays(nums2, nums1);}// 获取两个数组的大小int m = nums1.size(), n = nums2.size();// 初始化二分查找的边界int left = 0, right = m;// 开始二分查找,寻找最优分割点while (left <= right) {// i: 在nums1中的分割点,表示nums1左边有i个元素int i = (left + right) / 2;// j: 在nums2中的分割点,计算得出// (m+n+1)/2 确保左边部分元素总数是总元素数的一半int j = (m + n + 1) / 2 - i;// left1: nums1分割点左边的最大值(如果i=0,左边没有元素,设为INT_MIN)int left1 = (i == 0) ? INT_MIN : nums1[i - 1];// right1: nums1分割点右边的最小值(如果i=m,右边没有元素,设为INT_MAX)int right1 = (i == m) ? INT_MAX : nums1[i];// left2: nums2分割点左边的最大值(如果j=0,左边没有元素,设为INT_MIN)int left2 = (j == 0) ? INT_MIN : nums2[j - 1];// right2: nums2分割点右边的最小值(如果j=n,右边没有元素,设为INT_MAX)int right2 = (j == n) ? INT_MAX : nums2[j];// 检查当前分割是否满足条件:// nums1左边最大值 <= nums2右边最小值 且 nums2左边最大值 <= nums1右边最小值if (left1 <= right2 && left2 <= right1) {// 如果总元素个数是奇数if ((m + n) % 2 == 1) {// 中位数是左边两部分的最大值return max(left1, left2);} else {// 如果总元素个数是偶数// 中位数是(左边最大值 + 右边最小值)/ 2return (max(left1, left2) + min(right1, right2)) / 2.0;}} // 如果nums1左边部分太大,需要减少nums1左边的元素数量else if (left1 > right2) {// 向左移动nums1的分割点right = i - 1;} // 如果nums1左边部分太小,需要增加nums1左边的元素数量else {// 向右移动nums1的分割点left = i + 1;}}return 0.0;}
};时间复杂度O(LogN),空间复杂度O(1)
