思路
这道题我明显感觉之前做过, 但是不太记得是在哪里. 这个题目我也是走了个弯路才想出来的, 因为我之前做过这道题, 其实大致的思路还是有的. 我首先是想到了我上一次做这道题的时候使用的方法 :
- 对于这两个数组, 我通过不断地比较他们的中位数, 如果第一个中位数大于第二个, 那么说他们整体的中位数显然会出现在第一个中位数所在数组偏左的位置, 相当于第二个中位数偏右的位置, 这样的话我们可以在第一个数组右边和第二个数组左边切去相同数量的元素, 此时可以保证整体中位数的位置是不变的, 但是这里要特别注意我们不能切去n/2(n是较小的数组的大小), 而应该切去(n - 1) / 2, 至于为什么, 你可以举个例子, 你会发现当前中位数相邻的元素仍有可能变成新的中位数.
- 进过不断地切割导致最终较小的数组的长度变得 <=2, 此时无法再进行切割, 这也是我这种方式的缺陷, 就是切到2就要开始另外想办法求中位数, 这里我是强行合并了两个数组然后排序求中位数, 在第一个数组的长度远大于第二个数组时显然是不可取的. (显然不是O(log m + n))
- 所以最后的结果就是, 通过了所有的测试但是TLE.
这是代码, 如果对于最后那个一大一小数组求中位数有更好的方法应该是可以AC的 :
import java.util.Arrays;
public class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if(nums1.length == 0){
return findMid(nums2, 0, nums2.length);
}
else if(nums2.length == 0){
return findMid(nums1, 0, nums1.length);
}
if(nums1.length >= nums2.length){
return helper(nums1, 0, nums1.length, nums2, 0, nums2.length);
}
else{
return helper(nums2, 0, nums2.length, nums1, 0, nums1.length);
}
}
private double helper(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2){
double mid1 = findMid(nums1, start1, end1);
double mid2 = findMid(nums2, start2, end2);
if(mid1 == mid2) return mid1;
int length1 = end1 - start1;
int length2 = end2 - start2;
if(length2 <= 2){
int[] temp = new int[length1 + length2];
for(int i = 0; i < temp.length; ++i){
if(i < length1){
temp[i] = nums1[i + start1];
}else{
temp[i] = nums2[i + start2 - length1];
}
}
Arrays.sort(temp);
return findMid(temp, 0, temp.length);
}
int cutLength = (length2 - 1) / 2;
if(mid1 > mid2){
return helper(nums1, start1, end1 - cutLength, nums2, start2 + cutLength, end2);
}
else{
return helper(nums1, start1 + cutLength, end1, nums2, start2, end2 - cutLength);
}
}
private double findMid(int[] num, int start, int end){
System.out.println();
int halfIndex = (end - start) / 2 + start;
if((end - start) % 2 == 0){
return (double) (num[halfIndex] + num[halfIndex - 1]) / 2;
}
else{
return num[halfIndex];
}
}
}
其实在对第一种方式进行实现的时候我就回忆起了第二种方式 :
- 其实对于m, n大小的两个数组求解中位数, 问题完全可以转化为寻找第(m+n) / 2 的数(也有可能还需要第寻找(m + n)/ 2 - 1 的数).
- 其实寻找方式与第一种类似, 这里我就不多说了, 因为我发现我之前有一篇两个有序数组的中位数问题已经做了这道题...
实现
因为之前做过, 其实没什么好说的. 但是这里我用的helper函数其实求解的第k个元素其实是从0算起的第k个, 也就是说使我们正常逻辑中的第k+1个, 这样求解给我带来了很多麻烦, 例如后面++k之类的, 反正个人感觉应该还是我贴出来的链接中那种更加易于实现一点.
提交
第一次是最早的那个思路, TLE. 第二次才AC.
实现
public class Solution {
public static void main(String[] args) {
System.out.println(new Solution().findMedianSortedArrays(new int[]{1,2}, new int[]{3, 4}));
}
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int length = nums1.length + nums2.length;
if(length % 2 == 0){
return (helper(nums1, 0, nums1.length, nums2, 0, nums2.length, length / 2) +
helper(nums1, 0, nums1.length, nums2, 0, nums2.length, length / 2 - 1)) / 2.0;
}
else{
return helper(nums1, 0, nums1.length, nums2, 0, nums2.length, length / 2);
}
}
// findKth element
private double helper(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k){
int length1 = end1 - start1;
int length2 = end2 - start2;
if(length1 < length2) return helper(nums2, start2, end2, nums1, start1, end1, k);
if(length2 == 0) return nums1[k + start1];
if(k == 0) return nums1[start1] >= nums2[start2] ? nums2[start2] : nums1[start1];
// Kth element is in fact k + 1 th (count from 0)
++k;
int th2 = length2 >= k / 2 ? k / 2 : length2;
int th1 = k - th2;
int pos1 = start1 + th1;
int pos2 = th2 + start2;
if(nums1[pos1 - 1] >= nums2[pos2 - 1]){
return helper(nums1, start1, pos1, nums2, pos2, end2, th1 - 1);
} else {
return helper(nums1, pos1, end1, nums2, start2, pos2, th2 - 1);
}
}
}
最佳实现
目前还没找到我认为比上面这种解法还要简单的解法.