每日一道算法面试题(4):leetcode4 寻找两个有序数组的中位数

题目:给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。

请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

示例 1:

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

则中位数是 2.0
示例 2:

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

则中位数是 (2 + 3)/2 = 2.5

算法思路:

  1. 传统思路:用一个循环遍历两个数字,找到中间数字的位置,要注意区分 m+n 是奇数偶数的场景,用两个变量分别记录两个数字遍历的位置;算法比较简单,但算法复杂度为 O(m+n),不符合题目要求;
  2. 二分算法:根据题目算法复杂度的要求 O(log(m + n)),应该借助二分算法的思路才能满足要求,每次排除一半的数据。题目是求中位数,其实就是求第 k 小数的一种特殊情况,而求第 k 小数有一种算法。由于数列是有序的,我们可以一半儿一半儿的排除。假设我们要找第 k 小数,我们可以每次循环排除掉 k/2 个数:比较两个数组的第 k/2 个数字,如果 k 是奇数,向下取整;如果哪个小,就表明该数组的前 k/2 个数字都不是第 k 小数字,所以可以排除;依次类推,每次排除剩余的一半数据。注意 m+n 的和要考虑奇数和偶数,偶数时要找到中间的两个数字并取平均值;若是奇数,只需要找到一个中间数字即可。

算法代码:根据算法思路2,写出的算法具体代码如下:

public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        // 区分奇数偶数
        int middleLeft = (m + n + 1) / 2;
        if ((m + n) % 2 == 1) { // 奇数情况
            return getMiddleData(nums1, 0, m - 1, nums2, 0, n - 1, middleLeft);;
        }
        // 偶数情况
        int middleRight = (m + n + 2) /2;
        return (getMiddleData(nums1, 0, m - 1, nums2, 0, n - 1, middleLeft)
                + getMiddleData(nums1, 0, m - 1, nums2, 0, n - 1, middleRight)) * 0.5;
    }

    public static int getMiddleData(int[] num1, int start1, int end1, int[] num2, int start2, int end2, int k) {
        int len1 = end1 - start1 + 1;
        int len2 = end2 - start2 + 1;
        //让 len1 的长度小于 len2,这样就能保证如果有数组空了,一定是 len1
        if (len1 > len2) {
            return getMiddleData(num2, start2, end2, num1, start1, end1, k);
        }

        if (len1 <= 0) {
            return num2[start2 + k - 1];
        }

        if (k == 1) {
            return Math.min(num1[start1], num2[start2]);
        }

        int middleValue = k / 2; // 二分查找,每次排除一半数据
        // 定位数组1的位置
        int indexNum1 = len1 > middleValue ? (start1 + middleValue - 1) : end1;
        // 定位数组2的位置
        int indexNum2 = len2 > middleValue ? (start2 + middleValue - 1) : end2;
        if (num1[indexNum1] > num2[indexNum2]) { // 排除数组2 start2到indexNum2的数据
            k = k - (indexNum2 - start2 + 1); // 剩余的待定位数据数量
            return getMiddleData(num1, start1, end1, num2, indexNum2 + 1, end2, k);
        } else {  // 排除数组1 start1到indexNum1的数据
            k = k - (indexNum1 - start1 + 1); // 剩余的待定位数据数量
            return getMiddleData(num1, indexNum1 + 1, end1, num2, start2, end2, k);
        }
    }

如果你有疑问或更好的算法思路,欢迎留言交流!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值