[LeetCode] Median of Two Sorted Arrays 两个有序数组的中位数

题目

There are two sorted arrays nums1 and nums2 of size m and n respectively.

Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

You may assume nums1 and nums2 cannot be both empty.

Example 1:

nums1 = [1, 3]
nums2 = [2]

The median is 2.0
Example 2:

nums1 = [1, 2]
nums2 = [3, 4]

The median is (2 + 3)/2 = 2.5

解析

先回顾下中位数的概念,若数组长度为奇数,则中位数是中间那个。如果数组长度为偶数,中位数为中间两个数字的平均。由于m+n长度不确定,可以分情况讨论。有一个方法可以适用这两种情况,即们分别找第 (m+n+1) / 2 个,和 (m+n+2) / 2 个,然后求其平均值即可,这对奇偶数均适用。
因为时间复杂度要求为 O ( l o g ( m + n ) ) O(log (m+n)) O(log(m+n)),所以要采用二分法查找。比如查找某个数字,对于一个数组,二分法很简单,每次将数组二分,通过与中间元素比较,从而缩小查找范围。那么两个数组如何执行二分法查找?可以对K进行二分,即需要再nums1和nums2中分别查找第K/2个数字。令i和j分别是nums1和nums2的初始位置索引,这里有两种情况,可能两个数组都存在第K/2个数字,可能只有一个存在。当某个数组(比如nums1)不存在第K/2个数字,即i+K/2-1>nums.size(),那么忽略nums2的前K/2个数值,因为这种情况下,第K个数值不可能再nums2的前K/2个数值中。当均存在第K/2个数值midVal1和midVal2,则要比较midVal1和midVal2谁更小,小的数值所在的数组则可以忽略前K/2个元素。注意此时K也减小了K/2。通过这种判断,每次可以缩小一半的搜索范围。

C++解法一:

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size(), left = (m + n + 1) / 2, right = (m + n + 2) / 2;
        return (findKth(nums1, 0, nums2, 0, left) + findKth(nums1, 0, nums2, 0, right)) / 2.0;
    }
    int findKth(vector<int>& nums1, int i, vector<int>& nums2, int j, int k) {
    	// 当某一个数组所有数字被淘汰,则直接在另外一个数组中寻找中值
        if (i >= nums1.size()) return nums2[j + k - 1];
        if (j >= nums2.size()) return nums1[i + k - 1];
        // 当寻找第1个数值,直接比较
        if (k == 1) return min(nums1[i], nums2[j]);
        // 计算两个数组的第K/2个数
        int midVal1 = (i + k / 2 - 1 < nums1.size()) ? nums1[i + k / 2 - 1] : INT_MAX;
        int midVal2 = (j + k / 2 - 1 < nums2.size()) ? nums2[j + k / 2 - 1] : INT_MAX;
        // 比较midVal1和midVal2,缩小查找范围
        if (midVal1 < midVal2) {
            return findKth(nums1, i + k / 2, nums2, j, k - k / 2);
        } else {
            return findKth(nums1, i, nums2, j + k / 2, k - k / 2);
        }
    }
};

上面方法是采用i和j索引动态控制nums1和nums2的查找范围,数组本身没变。也可以在每次递归时根据索引范围的改变新建数组,但是这种方法可能超出时间复杂度限制。基本思路与上面类似,nums1和nums2分别查找第K/2个,哪一个数组的第K/2个更小,则忽略该数组前K/2个。有几种特殊情况,1)当某个数组为空,直接再另一个数组查找第K个,2)当K=1,直接比较nums[0]和nums[1]大小,返回小,3)当K/2大于数组长度,那么查找索引设置为数组长度,也就是返回数组最后一个。

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size();
        return (findKth(nums1, nums2, (m + n + 1) / 2) + findKth(nums1, nums2, (m + n + 2) / 2)) / 2.0;
    }
    int findKth(vector<int> nums1, vector<int> nums2, int k) {
    	// 1)当某数组为空
        if (nums1.empty()) return nums2[k - 1];
        if (nums2.empty()) return nums1[k - 1];
        // 2)当K=1
        if (k == 1) return min(nums1[0], nums2[0]);
        // 3) 索引位置
        int i = min((int)nums1.size(), k / 2), j = min((int)nums2.size(), k / 2);
        //4)比较nums1和nums2索引值,通过新建数组,缩小数组范围
        if (nums1[i - 1] > nums2[j - 1]) {
            return findKth(nums1, vector<int>(nums2.begin() + j, nums2.end()), k - j);
        } else {
            return findKth(vector<int>(nums1.begin() + i, nums1.end()), nums2, k - i);
        }
        return 0;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值