4. 寻找两个正序数组的中位数【困难】

题目

给定两个大小分别为 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;
    }
}

修补过程中也出现新的问题。注意

  1. 应当先处理"找到了要找的"这种情况,会容易很多。如果先处理数组越界,当同事满足越界和找到要找的两种条件时候,处理的情况会让人头疼,两种找到头的情况还要细改。
  2. 边界的确定。找到头的情况中重新为n1和n2赋值,此时应当找几个例子试一下,辅助确定好边界的确定条件。
  3. 出现几次解答错误是一个数组为空的情况。看了一下提示里面没有限制此种情况。最终发现还是数组越界情况下,n1、n2的赋值有问题。
  4. 找到头情况下的奇偶划分:奇数时n2和n2 + (total+1)%2作为下标指向同一元素,偶数时指向当前和下一个元素。全部直接除以2,免去了奇偶的单独区分。

题解放这,过两天再研究其他方法。题解指路


头一次死冲hard题,难哦。什么叫牵一发而动全身啊——解决一个bug 导致/发现 另一个bug,可真搞心态。
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值