leetcode第四题——寻找两个正序数组的中位数

题目信息

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数
示例 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

示例 3:

输入:nums1 = [0,0], nums2 = [0,0]
输出:0.00000

示例 4:

输入:nums1 = [], nums2 = [1]
输出:1.00000

示例 5:

输入:nums1 = [2], nums2 = []
输出:2.00000

提示:

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)) 的算法解决此问题吗?

解题思路

法1:暴力

用归并排序的方法在两个数组中找出前(m + n) / 2小的数和下一个小的数,算出中间两个数的平均数即可。
不太熟悉归并排序的同学可以看这里
优点
好理解,好实现。
缺点
相较于二分查找时间复杂度较高。
时、空复杂度分析
时间复杂度:O(m + n)(将所有归并排序的部分加起来,两个数组各遍历一遍,每个数需要O(1))。
空间复杂度:O(1)。

法2:二分查找

  • 只要让j = flag - i,i + j = flag的条件就满足了。
  • 然后只要在0 ~ m之间二分查找i的位置就行了。
  • 另外,二分查找的停止条件自然是l > r。那么该如何满足这个条件呢?就是当两个左边界的终点都≤两个右边界,即:
    ① l1 ≤ r1
    ② l1 ≤ r2※
    ③ l2 ≤ r1※
    ④ l2 ≤ r2

数组是有序的,所以①和④不需要考虑。
如果②不满足,说明l1大了,i应该往左走。
我们可以将i的右边界放到i的左边(即i不合法),那么i的左边界也必须往左走了(同时j往右了)。
同理,如果②不满足,j就需要往左走,即i往右走。
优点
时间快,也不难实现。
缺点
不是很好理解。
时、空复杂度分析
时间复杂度:O(log(min(m, n)))(选择两个数组中较短的二分会快一些)。
空间复杂度:O(1)。

代码实现

法1:暴力

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size(), flag = (m + n) / 2;
        int i = 0, j = 0, k = 0; // i、j表示nums1和nums2分别有多少个数的值是它们中前flag个小的
        // 归并排序
        while (i < m && j < n && k < flag) {
            if (nums1[i] <= nums2[j]) {
                ++i;
            } else {
                ++j;
            }
            ++k;
        }
        while (k < flag && i < m) {
            ++i;
            ++k;
        }
        while (k < flag && j < n) {
            ++j;
            ++k;
        }
        // 中间两个数(可能只有r)中左边的数
        // 从两个左边界中选一个合法并且值最大的
        int l = max(i == 0 ? INT_MIN : nums1[i - 1], j == 0 ? INT_MIN : nums2[j - 1]);
        // 右边/最中间的数
        // 从两个右边界中选一个合法并且值最小的
        int r = min(i == m ? INT_MAX : nums1[i], j == n ? INT_MAX : nums2[j]);
	        // 中间只有一个数就选r,两个数就选l和r的平均数,别忘了转化成小数
        return (m + n) % 2 == 1 ? r * 1.0 : (l + r) / 2.0;
    }
};

法2:二分查找

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size(), flag = (m + n) / 2;
        // 较短的数组二分更快。
        if (m > n) {
            return findMedianSortedArrays(nums2, nums1);
        }
        int i, j; // 右端起点,同时也是左端的长度
        int i_l = 0, i_r = m;
        int l1, r1, l2, r2;
        while (1) {
            i = (i_l + i_r) >> 1;     // (i_l + i_r) / 2的位运算版
            j = flag - i;
            // 详见解题思路部分
            if (i == 0) {
                l1 = INT_MIN;
            } else {
                l1 = nums1[i - 1];
            }
            if (i == m) {
                r1 = INT_MAX;
            } else {
                r1 = nums1[i];
            }
            if (j == 0) {
                l2 = INT_MIN;
            } else {
                l2 = nums2[j - 1];
            }
            if (j == n) {
                r2 = INT_MAX;
            } else {
                r2 = nums2[j];
            }
            if (l1 > r2) {
                i_r = i - 1;
            } else if (l2 > r1) {
                i_l = i + 1;
            } else {    // 四个条件均满足,则可跳出循环。
                break;
            }
        }
        // 中间两个数(可能只有r)中左边的数
        // 从两个左边界中选一个合法并且值最大的
        int l = max(l1, l2);
        // 右边/最中间的数
        // 从两个右边界中选一个合法并且值最小的
        int r = min(r1, r2);
        // 中间只有一个数就选r,两个数就选l和r的平均数,别忘了转化成小数
        return (m + n) % 2 == 1 ? r * 1.0 : (l + r) / 2.0;
    }
};
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

蒟蒻一枚

谢谢鸭~

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

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

打赏作者

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

抵扣说明:

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

余额充值