给定两个大小为 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
这题的主要难点在与时间复杂度,如果没有时间复杂度上的要去,做题十分简单,只要定义两个指针在num1和num2之间谁小谁移动,时间复杂度应该为(m+n)/2,但是题目涉及到了log,一般涉及到了log的复杂度都是用二分法去作答,想了半天没想到,看了看官方的,官方的解题思路过于严谨导致看不太懂,其实思路很简单。
就是因为是中位数所以肯定是在中间的,那么只要假设在num1中有一个数,下标为i,那么只要去num2[(m+n)/2-i]出找那个数是否小于num1[i],且
num2[m+n)/2-i+1]是大于num[i]的,
说明白点就是假设num1={1,3,5},num2={2,4},那么下标为1的num[1],首先在num1中大于了一个数,那么我只需要在num2中大于一个数,那么我就是中位数了,所以我只需要判断在num2中的大小关系即可判断是否为中位数。然后用二分在num1中找那个数就好了
我又去看了一遍官方的解题我好像把他的方法一理解错误了,因为我就看了一点点,看了找第k个数只要比k个数大就好了,就想到了这个方法,我以为官方的方法一讲的是这个,又看了方法二,还是方法二看的懂,我的算方法三吧,但也是二的主要思路。
方法二,大致是一样的,确实方法而就是对我想出来的方法的一种细分的思路,它也是分为两半,只要两个num的前一半的最大值小于后一半的最小值,那么答案就出来了,好像也不一样,他这个思路更加简洁且代码更加好打。
代码如下:
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if (nums1.length > nums2.length) {
return findMedianSortedArrays(nums2, nums1);
}
int m = nums1.length;
int n = nums2.length;
int left = 0, right = m, ansi = -1;
// median1:前一部分的最大值
// median2:后一部分的最小值
int median1 = 0, median2 = 0;
while (left <= right) {
// 前一部分包含 nums1[0 .. i-1] 和 nums2[0 .. j-1]
// 后一部分包含 nums1[i .. m-1] 和 nums2[j .. n-1]
int i = (left + right) / 2;
int j = (m + n + 1) / 2 - i;
// nums_im1, nums_i, nums_jm1, nums_j 分别表示 nums1[i-1], nums1[i], nums2[j-1], nums2[j]
int nums_im1 = (i == 0 ? Integer.MIN_VALUE : nums1[i - 1]);
int nums_i = (i == m ? Integer.MAX_VALUE : nums1[i]);
int nums_jm1 = (j == 0 ? Integer.MIN_VALUE : nums2[j - 1]);
int nums_j = (j == n ? Integer.MAX_VALUE : nums2[j]);
if (nums_im1 <= nums_j) {
ansi = i;
median1 = Math.max(nums_im1, nums_jm1);
median2 = Math.min(nums_i, nums_j);
left = i + 1;
}
else {
right = i - 1;
}
}
return (m + n) % 2 == 0 ? (median1 + median2) / 2.0 : median1;
}
}
官方的这个代码也太精细了,一点多余的代码量的没有,就连那个if里面的判断也用数学方法给弄成了一个判断而已,我还在i的取值中迷惑了半天,到底怎么取,看了看官方的知道,官方的逻辑太清晰了,
官方解析链接
这个链接得保存,这题有点经典的韵味我觉得这题出的够好。