LeetCode 4. 寻找两个有序数组的中位数(Java)

  1. 寻找两个有序数组的中位数

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。

示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0

示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方法一:暴力求解,时间复杂度(m+n),合并数组,如果nums1.length+nums2.length的和为奇数,则中位数为下标为(nums1.length+nums2.length)/2的数,如果为偶数,则中位数为下标(nums1.length+nums2.length)/2和(nums1.length+nums2.length)/2-1的数的平均数。

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        //合并数组,
        //如果nums1.length+nums2.length的和为奇数,则中位数为下标为(nums1.length+nums2.length)/2的数
        //如果为偶数,则中位数为下标(nums1.length+nums2.length)/2和(nums1.length+nums2.length)/2-1的数的平均数
        double median=0;
        //如果nums1为空,则将nums2传入函数res求解
        if(nums1.length==0)
        {
            median=res(nums2);
            return median;
        }
        //如果nums2为空,则将nums1传入函数res求解
        if(nums2.length==0)
        {
            median=res(nums1);
            return median;
        }
        //新建一个数组用来存储合并出的新数组
        int[] nums=new int[nums1.length+nums2.length];
        int i=0;
        int j=0;
        int k=0;
        while(k<nums.length)
        {
            if(i==nums1.length)//如果i到达nums1的尾部
            {
                while(j!=nums2.length)//而j没有到达nums2的尾部时
                {
                    nums[k++]=nums2[j++];//将剩余的nums2加入新数组的尾部
                }
                break;
            }
            if(j==nums2.length)//如果j到达nums2的尾部
            {
                while(i!=nums1.length)//而i没有到达nums1的尾部时
                {
                    nums[k++]=nums1[i++];//将剩余的nums1加入新数组的尾部
                }
                break;
            }
            if(nums1[i]<nums2[j])//如果nums1[i]小于nums2[j]
            {
                nums[k++]=nums1[i++];//则将nums1[i]存入新数组
            }
            else
            {
                nums[k++]=nums2[j++];//否则将nums2[j]存入新数组
            }
        }
        //将新数组传入函数res求解
        median=res(nums);
        return median;
    }
    public double res(int[] num)
    {
        double number=num[0];        
        if(num.length%2==0)//如果数组长度为偶数
        {
            number=(num[num.length/2]+num[(num.length/2)-1])/2.0;//则中位数为中间两个数的平均数
        }
        else
        {
            number=num[num.length/2];//如果数组长度为奇数,则中位数为中间那个数
        }
        return number;
    }
}

方法二:二分法,时间复杂度 O(log(m + n)),求中位数,其实就是求第 k 小数的一种特殊情况。采用递归的思路,每次排除有三种可能:(1)两个数组的长度都大于k/2,则每次递归排除掉k/2个元素,比较两个数组在第k/2位置的数字,谁的小,就把对应数组的前k/2个数字排除,然后将新生成的数组和没有改变的数组进入递归,k要减去排除的数字的个数也传入递归;(2)只要有一个数组的长度小于k/2,则比较这个数组尾部数字与另一个数组的第k/2个数字的大小,如果这个数组尾部数字小于另一个数组的第k/2个数字,则排除掉这个数组剩余的所有元素,将空数组和没改变的数组进入递归,k要减去排除的数字的个数也传入递归;否则,排除掉另一个数组的前k/2个元素,将新生成的数组和没有改变的数组进入递归,k要减去排除的数字的个数也传入递归。(3)如果两个数组的长度都小于k/2,则比较两个数组的尾部数字,哪个数组的尾部数字小,则排除该数字的剩余所有元素,将空数组和没改变的数组进入递归,k要减去排除的数字的个数也传入递归。

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m=nums1.length;
        int n=nums2.length;
        int left=(m+n+1)/2;
        int right=(m+n+2)/2;
        //将数组长度为奇数和为偶数合并为一种情况,如果为奇数,则计算两次同样的k
        return (FMSA(nums1,0,m-1,nums2,0,n-1,left)+FMSA(nums1,0,m-1,nums2,0,n-1,right))/2.0;
    }
    private int FMSA(int[] nums1,int start1,int ends1,int[] nums2,int start2,int ends2,int k)
    {
        int len1=ends1-start1+1;
        int len2=ends2-start2+1;
        //让len1为短数组,则以后若有数组长度为0,则一定是nums1
        if(len1>len2)
        {
            return FMSA(nums2,start2,ends2,nums1,start1,ends1,k);
        }
        //如果数组长度为0,则返回nums2中第k小的数
        if(len1==0)
        {
            return nums2[start2+k-1];
        }
        //如果k为1,则比较两个数组头的大小,取小的那个为结果      
        if(k==1)
        {
            return Math.min(nums1[start1],nums2[start2]);
        }
        //将i指向nums1的第k/2个元素,或者,如果nums1的长度小于k/2,则指向nums1的尾部
        int i=start1+Math.min(len1,k/2)-1;
        //将j指向nums2的第k/2个元素,或者,如果nums2的长度小于k/2,则指向nums2的尾部
        int j=start2+Math.min(len2,k/2)-1;
        //比较i和j所指向的两个数字的大小
        if(nums1[i]>nums2[j])
        {
            return FMSA(nums1,start1,ends1,nums2,j+1,ends2,k-(j-start2+1));
        }
        else
        {
            return FMSA(nums1,i+1,ends1,nums2,start2,ends2,k-(i-start1+1));
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值