LeetCode第4题-寻找两个正序数组的中位数-java实现-图解思路与手撕代码

LeetCode第4题-寻找两个正序数组的中位数-java实现-图解思路与手撕代码


一、题目描述

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

算法的时间复杂度应该为 O(log (m+n)) 。

二、解题思路与代码实现

1.二分法思路

最自然的思路是将两个数组合并成一个长度为m+n的有序数组,然后按照m+n奇偶性输出结果,但是不满足时间复杂度的要求,我们需要用二分法的思想解决问题。

二分法思路:

比如我们需要求第k个数字

(如果m+n为奇数,则k=(m+n)/2,如果m+n为偶数,则k分别等于(m+n)/2和(m+n)/2+1)

那么比较两个数组第k/2位的数字,由于数组是有序的,并且从小到大排序,所以第k/2位的数字比较小的那个数组可以在接下来计算中不用比较前k/2位,这里体现了二分法求解的思想。

具体实现如下:

定义一个函数findMidNum,这个函数传入两个数组,两个数组比较的范围(比如0-m和0-n),以及需要求取的第k位数字。

分别求取初始数组的第k=(m+n+1)/2位和第k=(m+n+2)/2位数字,如果(m+n)是奇数,则返回两个相同的数字,我们相加除以2,如果(m+n)是偶数,则返回两个不同的数字,正好是中位线两侧的数字,相加除以2即是所求结果。

2.代码实现

代码如下(示例):

如果两个数组的总长度是奇数,则返回两个一样的数字,如果总长度是偶数,则返回两个不一样的数字

	private static double findMedianSortedArraysWithLog(int[] nums1, int[] nums2) {
        return (findMidNum(nums1, 0, nums1.length, nums2, 0, nums2.length, (nums1.length + nums2.length + 1) / 2) +
                findMidNum(nums1, 0, nums1.length, nums2, 0, nums2.length, (nums1.length + nums2.length + 2) / 2)) * 0.5;
    }

findMidNum函数的代码细节如下所示:

该函数采用递归的方式。

在递归过程中首先判断nums1和nums2哪个数组最小,将最小的数组放在nums1中。

然后判断nums1的比较范围是否为空,如果为空直接返回nums2数组对应位置的数字。

然后判断k是否为1,如果为1,比较两个数组比较范围中的第一个值,并返回最小值。

注:此处的k在代码中是midLen,代表表示中值意义的长度。

	private static double findMidNum(int[] nums1, int l1, int r1, int[] nums2, int l2, int r2, int midLen) {
        if (r1 - l1 > r2 - l2) {
            return findMidNum(nums2, l2, r2, nums1, l1, r1, midLen);
        }
        if (l1 >= r1) {
            return nums2[l2 + midLen - 1];
//            return nums2[l2 + midLen];
        }
        if (midLen == 1) {
            return Math.min(nums1[l1], nums2[l2]);
        }
        int i = Math.min(r1 - 1, l1 + midLen / 2 - 1);
        int j = Math.min(r2 - 1, l2 + midLen / 2 - 1);
        if (nums1[i] < nums2[j]) {
            return findMidNum(nums1, i + 1, r1, nums2, l2, r2, midLen - (i + 1 - l1));
        } else {
            return findMidNum(nums1, l1, r1, nums2, j + 1, r2, midLen - (j + 1 - l2));
        }
    }

做完上述三个判断,接下来取两个数组比较的数字,下标公式是i = Math.min(r1 - 1, l1 + midLen / 2 - 1)和j = Math.min(r2 - 1, l2 + midLen / 2 - 1),两个公式都是求两个值的最小值,右项是比较范围左端加上k/2,再减去1,左侧是比较范围的最右侧,以防超出数组范围。

比较两个数组对应位置数字的大小,比较小的那个数组对其更新比较范围,比较范围右端不变,左端变成i+1 。k值也需要更新,k减去比较范围左端减少的长度。


总结

这道题的难度在于难以把复杂问题抽象化,写出的代码往往需要多次判断,极其容易遗漏比较条件,而上述代码优点在于规避了奇偶性,通过采用递归的方法达到简化代码的目的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值