题目
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
提示:
~~~~
nums1.length == m
~~~~
nums2.length == n
~~~~
0 <= m <= 1000
~~~~
0 <= n <= 1000
~~~~
1 <= m + n <= 2000
~~~~
-106 <= nums1[i], nums2[i] <= 106
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
此题为hard,个人认为最困难的部分,一是不同情况的分类,二是下标界线的确定。
初次尝试(漏洞很多)
很容易按照归并排序的方法去思考,双指针在两个数组中不断向后。查找是一个循环,后面分为三种情况依次处理。
循环查找:
while(n1 < nums1.length && n2 < nums2.length && i < (total+1)/2){//基本情况下出循环的条件是(total+1)/2 - 1,此时对应的就是要找的
if(nums1[n1] < nums2[n2] && nums1[n1] > min){
n1++;
flag = 1;
}else if(nums1[n1] > nums2[n2] && nums2[n2] > min){
n2++;
flag = 2;
}
i++;
}
分情况处:1:找到要找的了
if(i >= (total+1)/2){
//两个数组都占着
if(total%2 == 0){
flag = nums1[n1]>nums2[n2]?flag:(3-flag);
if(flag == 1){
return (nums1[--n1] + nums2[n2])/2.0;
}else if(flag == 2){
return (nums1[n1] + nums2[--n2])/2.0;
}
}else if(flag == 1){
return nums1[--n1]/1.0;
}else if(flag == 2){
return nums2[--n2]/1.0;
}
}
分情况处理2和3:nums1 或者nums2越界,要找的在其中一边
if(n1 >= nums1.length){//都在nums2数组中
temp = (total+1)/2 - n1 - 1;
return (nums2[temp] + nums2[temp + (total+1)%2])/2.0;
}else if(n2 >= nums2.length){//都在nums1数组中
temp = (total+1)/2 - n2 - 1;
return (nums1[temp] + nums1[temp + (total+1)%2])/2.0;
}
随着以下例子没有通过,宣告此方法破产o(╥﹏╥)o。
输入:
[1,2]
[-1,3]
输出:
2.00000
预期结果:
1.5
以上例子描述的情况是总数为偶数,需要两个中位数,而两个中位数恰好在一个数组中。初次尝试是有缺陷的。
修补完成
初次尝试的缺陷是在总数偶数情况下,想当然地认为找到了第一个中位数,另一个中位数为本数组下一个(另一数组越界)或者另一个数组的当前下标所指(两数组都没有越界)。
也就通过以上例子暴露出情况划分是有问题的。
修补如下,成功通过:
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int total = nums1.length + nums2.length;
int n1 = 0, n2 = 0;
int i = 0;
int min = 0;//设为任何值都可以,后面必然会赋值
//跳出循环就三种条件:nums1找到头了、nums2找到头了、找到要找的了
//条件设为i < (total+1)/2:总数偶数时直接找到两个中位数中较小一个,再比较得到下一个就妥了
while(n1 < nums1.length && n2 < nums2.length && i < (total+1)/2){
if(nums1[n1] < nums2[n2]){
min = nums1[n1];
n1++;
}else{
min = nums2[n2];
n2++;
}
i++;
}
//找到了要找的
if(i >= (total+1)/2 && total%2 == 0){//总数偶数,需要比较得到两个中的下一个中位数
if(n2 >= nums2.length){
return (min + nums1[n1])/2.0;
}else if(n1 >= nums1.length){
return (min + nums2[n2])/2.0;
}else if(nums1[n1] < nums2[n2]){
return (min + nums1[n1])/2.0;
}else{
return (min + nums2[n2])/2.0;
}
}else if(i >= (total+1)/2 && total%2 == 1){//总数奇数,直接返回
return (float)min;
}
if(n1 >= nums1.length){//nums1找到头了,都在nums2数组中
n2 = (total-1)/2 - n1;
return (nums2[n2] + nums2[n2 + (total+1)%2])/2.0;
}else if(n2 >= nums2.length){//nums2找到头了,都在nums1数组中
n1 = (total-1)/2 - n2;
return (nums1[n1] + nums1[n1 + (total+1)%2])/2.0;
}
return 0;
}
}
修补过程中也出现新的问题。注意
- 应当先处理"找到了要找的"这种情况,会容易很多。如果先处理数组越界,当同事满足越界和找到要找的两种条件时候,处理的情况会让人头疼,两种找到头的情况还要细改。
- 边界的确定。找到头的情况中重新为n1和n2赋值,此时应当找几个例子试一下,辅助确定好边界的确定条件。
- 出现几次解答错误是一个数组为空的情况。看了一下提示里面没有限制此种情况。最终发现还是数组越界情况下,n1、n2的赋值有问题。
- 找到头情况下的奇偶划分:奇数时n2和n2 + (total+1)%2作为下标指向同一元素,偶数时指向当前和下一个元素。全部直接除以2,免去了奇偶的单独区分。
题解放这,过两天再研究其他方法。题解指路
头一次死冲hard题,难哦。什么叫牵一发而动全身啊——解决一个bug 导致/发现 另一个bug,可真搞心态。