2021-10-15

力扣算法题:寻找两个正序数组的中位数

链接

解法一:

利用中位数是在一列数字中排中间的那一位,我们便可以一步一步地,两个数组分别进行 ++ 操作,直至遇见中位数。

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        //第二种解法,利用中位数在两个数组中是第 k 小

        int len = (nums1.length + nums2.length) ;       //两个数组的总长度
        Arrays.sort(nums2);
        Arrays.sort(nums1);
        int left = 0,right = 0;
        int astart = 0,bstart = 0;
        for(int i = 0;i<=len/2;i++){
            left = right;
            if(astart < nums1.length && (bstart >= nums2.length || nums1[astart] < nums2[bstart])){
                right = nums1[astart++];
            }else {
                right = nums2[bstart++];
            }
        }
        if(len % 2 == 0){
            return ((float)left + (float)right) /2;
        }else{
            return right;
        }

    }
}

在这里,left 和 right 的设置正是为了解决数组的总长度是奇偶不同的情况,left 总是记录先前 right 的位置,当数组的长度为偶数的时候,中位数正是 (left + right) /2

同时,当数组a进行 ++ 操作的时候,aStart 的值必须小于a的长度,同时需要a[aStart] 的值小于 b[bStart] 的值,否则的话则bStart++

当然,另一种情况便是b数组的长度已经到头,a数组便可在 i 内进行 ++ 操作,因此:

astart < nums1.length && (bstart >= nums2.length || nums1[astart] < nums2[bstart])            

解法一的时间复杂度为 O(m+n)

解法二:
这正是解法一的改版,从一个一个排除升级为一片一片地排除。

设中位数为k, 我们可以每次排除最多 k/2 的长度,这个长度不影响后续操作。
情况有多种:

每次取两个数组的第 k/2 个元素进行比较,把小的一方的前 k/2 个元素悉数排除。

若是 k/2 的长度已经超过其中一方数组的长度,我们则可以弹性选取较小的数组长度作为比较点进行互相比较,同样排除这个比较点大小的元素。
之后便开始递归以上操作直至找到中位数

在每次排除之后,k的值也要相应进行更新,减去已经排除的数量,并且每次都要更新被排除数组的起始位置。

class Solution {
    public  double findMedianSortedArrays(int[] nums1, int[] nums2) {
        //第三种解法,设中位数为第k个,一次排除 k/2 的数量。

        int len = (nums1.length + nums2.length) ;       //两个数组的总长度
        Arrays.sort(nums2);
        Arrays.sort(nums1);

        
        //考虑数组总长度奇偶的情况,奇数则两个结果相同,偶数则为中间的两个数,得到的是 第 k 个数而不是索引
        int left = (len + 1) / 2;
        int right = (len + 2) / 2;
        
        //  *0.5 防止其向下取整
        return((justDoIt(nums1,0,nums1.length-1,nums2,0,nums2.length-1,left) + justDoIt(nums1,0,nums1.length-1,nums2,0,nums2.length-1,right) )*0.5 );
    }

    public  int justDoIt(int a[],int aStart,int aEnd,int b[],int bStart,int bEnd,int k){
        int m = aEnd - aStart+1;
        int n = bEnd - bStart+1	;

        //首先保证 a 数组的长度小于 b 数组的长度,这样省事很多。
        //当数组的长度为0的时候,就不纳入考虑
        if(m > n){
            return justDoIt(b,bStart,bEnd,a,aStart,aEnd,k);
        } 

       
        //case 1 :数组 a 的长度为0,直接不用后面的事
        if(m == 0 ){
            return b[bStart + k-1];
        }
        
        //case 2: 中位数为 第1个(可能为递归后的结果)
        if(k == 1){
            return Math.min(b[bStart],a[aStart]);
        }

        //一般 case:
        
        //将数组的长度和 k/2 做对比,若数组长度小于 k/2 的话,则选取数组的长度,舍去的部分也是二者之一
        int i = aStart + Math.min((k / 2) ,m) -1;  	
        int j = bStart + Math.min((k / 2) ,n) -1;  	//由于是索引,所以 -1

        //若 a 数组中前 k/2 个数  小于 b 的,则要舍去小的部分
        if(a[i] < b[j]){
        	//此处 a 的开始为 i+1 而非 aStart + i + 1,是因为舍弃的数量加上1 就是 新的起始位置
            return justDoIt(a, i +1,aEnd,b,bStart,bEnd,k-( i+1 - aStart ) );		//减去舍去的长度再度二分法
        }else{
            return justDoIt(a,aStart,aEnd,b,j +1,bEnd,k- (j+1-bStart) );		//b小,舍去b
        }
    }
}

解法二的时间复杂度为 O(log(m+n))

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值