【练题记录】leetcode 4. 寻找两个有序数组的中位数

25 篇文章 0 订阅
3 篇文章 0 订阅

问题描述

给定两个大小为 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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

  • 1、由于题目要求时间复杂度为O(log(m + n)),因此要使用二分搜索
  • 2、由于nums1和nums2是从小到大排好序的,因此我们要利用好这个性质
    • 先处理nums1或者nums2为空的情况,这个测试用例中有,需要特别注意
    • 处理nums1和nums2没有交叉的情况
    • 对于nums1和nums2右交叉的情况,按照中位数的定义,我们可以对nums1和nums2按照左右分割的方式处理,如下
left_part                            |        right_part
nums1[0], nums1[1], ..., nums1[i-1]  |  nums1[i], nums1[i+1], ..., nums1[m-1]
nums2[0], nums2[1], ..., nums2[j-1]  |  nums2[j], nums2[j+1], ..., nums2[n-1]

我们只需left_part,right_part满足以下条件即可

1) len(left_part) == len(right_part)
2) max(left_part) <= min(right_part)

这样我们可以推出i,j,m,n的关系

i+j=(m + n + 1) / 2

于是,只需要决定i,那么j自然就可以求得
求得i,j之后,再按照合并后的数组长度m+n的奇偶性来求中位数

当m+n为奇数时,中位数为max(left_part) 
当m+n为偶数时,中位数为(max(left_part)+min(right_part))/2

代码实现

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        if (nums1.length == 0 && nums2.length == 0) {
            return 0;
        }
        if (nums1.length == 0) {
            return handleNoIntersect(nums1, nums2);
        }
        if (nums2.length == 0) {
            return handleNoIntersect(nums2, nums1);
        }

        //如果第一个数组的最大值比第二个数组的最小值还小
        if (nums1[nums1.length - 1] <= nums2[0]) {
            return handleNoIntersect(nums1, nums2);
        }
        //如果第二个数组的最大值比第一个数组的最小值还小
        if (nums1[0] >= nums2[nums2.length - 1]) {
            return handleNoIntersect(nums2, nums1);
        }

        //根据nums1Left来决定nums2Left,因此nums1的长度越短,算法效率越高
        if (nums1.length > nums2.length) {
            int[] tempNums = nums1;
            nums1 = nums2;
            nums2 = tempNums;
        }

        //nums1的左半部分的元素数目
        int nums1Left = 0;
        int nums1LeftLower = 0;
        int nums1LeftHigh = nums1.length;
        //nums2的左半部分的元素数目
        int nums2Left = 0;

        int totalHalfLength = (nums1.length + nums2.length + 1) / 2;

        while (nums1LeftLower <= nums1LeftHigh) {
            nums1Left = (nums1LeftLower + nums1LeftHigh) / 2;
            nums2Left = totalHalfLength - nums1Left;
            //对nums1,左边无元素,所以只用比较nums2的左边和nums1的右边
            if (nums1Left == 0) {
                if (nums2[nums2Left - 1] > nums1[nums1Left]) {
                    nums1LeftLower = nums1Left + 1;
                    continue;
                } else {
                    break;
                }

            }
            //对nums1,右边无元素,所以只用比较nums1的左边和nums2的右边
            if (nums1Left == nums1.length) {
                if (nums1[nums1Left - 1] > nums2[nums2Left]) {
                    nums1LeftHigh = nums1Left - 1;
                    continue;
                } else {
                    break;
                }

            }

            if (nums1Left >= nums1LeftLower && nums2[nums2Left - 1] > nums1[nums1Left]) {
                //如果nums2的左边大于nums1的右边,说明nums1的右边取小了
                nums1LeftLower = nums1Left + 1;
                continue;
            }
            if (nums1Left <= nums1LeftHigh && nums1[nums1Left - 1] > nums2[nums2Left]) {
                //如果nums1的左边大于nums2的右边,说明nums1当前的左边取大了
                nums1LeftHigh = nums1Left - 1;
                continue;
            }
            //nums1Left取的正好
            break;
        }

        // 如果加起来是偶数个,就取左侧的最大数和右侧的最小数之和的平均值
        if ((nums1.length + nums2.length) % 2 == 0) {
            return (maxLeftValue(nums1Left, nums1, nums2Left, nums2) + minRightValue(nums1Left, nums1, nums2Left, nums2)) / 2.0;
        } else {
            // 如果加起来是奇数个,就取左侧的最大数
            return maxLeftValue(nums1Left, nums1, nums2Left, nums2);
        }
    }

    /**
     * 处理两个数组不相交的情况
     */
    private double handleNoIntersect(int[] leftArray, int[] rightArray) {
        int leftPosition = (leftArray.length + rightArray.length + 1) / 2;
        int rightPosition = (leftArray.length + rightArray.length + 2) / 2;
        int leftValue;
        int rightValue;
        if (leftPosition <= leftArray.length) {
            leftValue = leftArray[leftPosition - 1];
        } else {
            leftValue = rightArray[leftPosition - leftArray.length - 1];
        }

        if (rightPosition <= leftArray.length) {
            rightValue = leftArray[rightPosition - 1];
        } else {
            rightValue = rightArray[rightPosition - leftArray.length - 1];
        }
        return ((double) leftValue + (double) rightValue) / 2;
    }

    private int maxLeftValue(int nums1Left, int[] nums1, int nums2Left, int[] nums2) {
        if (nums1Left == 0) {
            return nums2[nums2Left - 1];
        }
        if (nums2Left == 0) {
            return nums1[nums1Left - 1];
        }
        return Math.max(nums1[nums1Left - 1], nums2[nums2Left - 1]);
    }

    private int minRightValue(int nums1Left, int[] nums1, int nums2Left, int[] nums2) {
        if (nums1Left == nums1.length) {
            return nums2[nums2Left];
        }
        if (nums2Left == nums2.length) {
            return nums1[nums1Left];
        }
        return Math.min(nums1[nums1Left], nums2[nums2Left]);
    }
  • 这个题目的测试用例中对边界条件的测试比较多,边界条件一定要处理好
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值