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

难度:困难

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数

算法的时间复杂度应该为 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

这个题看起来是不难哈,已经给排好序的两个数组,找一下中位数,那为啥是困难难度的题呢?你看嘛,他要求时间复杂度为O(log(m+n)),甚至不是O(m+n)!变态啊!

那种把数组合起来找中位数的不符合题目要求的解法就不做了哈,这种傻子都会,归并排序的归并过程嘛,不会有人还不会吧。

不过也还是可以掌握一下,因为有时候机考,你能过用例就行,那么如果面试的时候,你用那种方法,面试官可能都惊呆了,他可能会以为你以为他是个傻*。当然你也可以用指针的方式走,走到指针走过的路等于中位数的下标的话,确实比归并好一些,毕竟归并的空间复杂度会高一些。

显然,无论是归并还是双指针,都无法满足题目要求的时间复杂度。

O(log(m+n))得用二分法来做了。

二分查找法在解决这个问题时的核心思想是将两个数组的元素进行比较,逐步缩小查找范围,直到找到中位数。

详细步骤解释:

确定中位数的位置:两个数组长度之和为偶数时,中位数是中间两个数的平均值;为奇数时,中位数是中间的那个数。因此,我们需要找到第 (m+n+1)/2 个数(奇数情况)和第 (m+n+2)/2 个数(偶数情况),其中 m 和 n 分别是两个数组的长度。

使用二分查找:要找出第k小的数,初始化数组指针为k/2,此时两个指针右边的数其实是k个,比较两个数组k/2位置的数值,如果其中一个小,则将其左边的值全部抛除,更新指针,始终保证两个指针右边的数加起来有一半的数量,继续比较。

边界条件处理:当有一个数组的指针超出数组边界时,说明第 k 小的数在另一个数组中。

题解:

class Solution {
public:
    int getKth(vector<int>& nums1, int i, vector<int>& nums2, int j, int k) {
        // 当一个数组为空时,直接在另一个数组中找第 k 个数
        if (i >= nums1.size())
            return nums2[j + k - 1];
        if (j >= nums2.size())
            return nums1[i + k - 1];

        // 当 k 为 1 时,返回两个数组中较小的那个数
        if (k == 1)
            return min(nums1[i], nums2[j]);

        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;

        // 递归地在较小的部分中查找第 k 小的数
        if (midVal1 < midVal2) {
            return getKth(nums1, i + k / 2, nums2, j, k - k / 2);
        } else {
            return getKth(nums1, i, nums2, j + k / 2, k - k / 2);
        }
    }
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size();
        int n = nums2.size();
        if ((m + n) % 2)
        {
            return getKth(nums1, 0, nums2, 0, (m + n + 1) / 2);
        }
        int left = (m + n) / 2;
        int right = (m + n + 2) / 2;

        // 使用二分查找法分别找到第 left 和 right 小的数
        return (getKth(nums1, 0, nums2, 0, left) +
                getKth(nums1, 0, nums2, 0, right)) /
               2.0;
    }
};

提交结果:

在这里插入图片描述
复杂度分析:
在这里插入图片描述
更多技术干货,尽在公众号“系统编程语言”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GarenJian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值