2021-5-4 第四天第四题

寻找两个正序数组的中位数

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数

输入: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

输入:nums1 = [0,0], nums2 = [0,0]
输出:0.00000

输入:nums1 = [], nums2 = [1]
输出:1.00000

输入:nums1 = [2], nums2 = []
输出:2.00000

Version1:创建一个新的数组存储原来两个数组的值,然后查找中位数
public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
    //整体思路:将nums1和nums2中的值赋值到一个新的数组,新数组的大小为原来两数组长度的和。从新得到的数组中就很容易得到中位数了。
    int len1=nums1.length;
    int len2=nums2.length;
    int len=len1+len2;
    int[] arr=new int[len];
    //定义两个指针:start1和start2,start1表示数组1的移动下标,start2表示数组2的移动下标
    int start1=0;
    int start2=0;
    //ans表示最终的结果:中位数
    double ans=0;
    for(int i=0;i<len;i++){
        if(start1<len1&&start2<len2){//数组1和数组2都没有到最后
            if(nums1[start1]<=nums2[start2]){//根据start1和start2位置数的大小,确定将那个数                                                插入到新数组中,并将指针移动到下一个位置。
                arr[i]=nums1[start1];
                start1++;
            }else{
                arr[i]=nums2[start2];
                start2++;
            }
        }else if(start1<len1){//数组2已经遍历完成了,将数组1剩余的值复制到新数组中
            arr[i]=nums1[start1];
            start1++;
        }else{//数组1已经遍历完成了,将数组2剩余的值复制到新数组中
            arr[i]=nums2[start2];
            start2++;
        }
    }
    if(len%2==1){
        ans=arr[len/2];
    }else{
        ans=(arr[len/2-1]+arr[len/2])/2.0;
    }
    return ans;
}

执行用时:3 ms, 在所有 Java 提交中击败了82.29% 的用户

内存消耗:39.5 MB, 在所有 Java 提交中击败了88.64% 的用户

Version2 利用二分法调用递归
	public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m=nums1.length;
        int n=nums2.length;
        int left=(n+m+1)/2;
        int right=(n+m+2)/2;
        
        //m+n的结果分为两种情况,和为奇数;和为偶数。下面这行代码将两种情况合成一种情况,不用进行分类讨论了。
        //当m+n为奇数,中位数则取中间值。(n+m+1)/2=中位数,而(n+m+2)/2=(n+m+1)/2+(1/2)=取整后为(n+m+1)/2,二者的和再除以2还为中位数。
        //当m+n为偶数,中位数则取中间两个数的平均值。(n+m+1)/2=(n+m)/2+(1/2)取整后为(n+m)/2;(n+m+2)/2=(n+m)/2+1;正好是中间的两个数,和的一半即为中位数。
        return (getKth(nums1,0,m-1,nums2,0,n-1,left)+getKth(nums1,0,m-1,nums2,0,n-1,right))/2.0;
    }

    private static int getKth(int[] nums1,int start1,int end1,int[] nums2,int start2,int end2,int k){
        int len1=end1-start1+1;
        int len2=end2-start2+1;
        
        //如果第一个数组长度大于第二个字符长度,交换两个数组的位置,始终让较小的数组放在前面,这样可以保证当数组长度为0时,是第一个数组。
        if(len1>len2){
            return getKth(nums2,start2,end2,nums1,start1,end1,k);
        }
        
        //如果第一个数组长度为0时,直接求第二数组的从start2起的第k-1个数。
        //递归终止条件1:
        if(len1==0){
            return nums2[start2+k-1];
        }
        
        //递归终止条件2:
        //k==1,表示只需要求出第一个小的数即可,因此比较两个数组中第一个数的大小,返回较小的数。
        if(k==1){
            return Math.min(nums1[start1],nums2[start2]);
        }
        
        //对于第一个数组的第i个值分为2种情况
        //1.len1<k/2:因此比较数组1的最后一个数和第二个数组的k/2个数
        //2.len1>k/2;因此比较数组1的k/2个数和第二个数组的k/2个数
        int i=start1+Math.min(len1,k/2)-1;
        
        //对于j来说是属于第二个数组的,因此不需要比较len2,k/2的大小,肯定是k/2。
//        int j=start2+Math.min(len2,k/2)-1;
        
        int j=start2+(k/2)-1;
        //调用递归,每次砍去一半值。
        
        if(nums1[i]>nums2[j]){
            return getKth(nums1,start1,end1,nums2,j+1,end2,k-(j-start2+1));
        }else{
            return getKth(nums1,i+1,end1,nums2,start2,end2,k-(i-start1+1));
        }
    }
总结

1.求中位数的下标时,可以借鉴如何将奇数和偶数合二为一的方式,就不用单独进行讨论了。

int first=(n+1)/2;//n为数组的长度
int second=(n+2)/2;
int medium=(first+second)/2;

2.当两个数组相加时,当涉及到数组先计算完和后计算完(即数组长短的问题),可以人为的设置数组长度短的在前面,数组长的在后面,也可以简化分类操作。

if(len1>len2){
    return getKth(nums2,start2,end2,nums1,start1,end1,k);
}

3.使用递归时,要设置好递归结束的条件。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值