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减去比较范围左端减少的长度。
总结
这道题的难度在于难以把复杂问题抽象化,写出的代码往往需要多次判断,极其容易遗漏比较条件,而上述代码优点在于规避了奇偶性,通过采用递归的方法达到简化代码的目的。