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

题目

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

算法的时间复杂度应该为 O(log (m+n)) 。

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

解题思路

上一篇文章讲了个简单的二分查找的变形,查找元素的位置,变形为查找第一个大于等于指定元素的下标。再来看这个题,题目两个数组有序,要求时间复杂度log级别,因此也是一道考察二分查找的变形题目,关键在于如何二分。这里题目,要求找出并返回这两个正序数组的 中位数 。我们不妨用另一种思路,题目是求中位数,其实就是求第 k 小数的一种特殊情况,而求第 k 小数有一种算法。
由于数列是有序的,其实我们完全可以一半儿一半儿的排除。假设我们要找第 k 小数,我们可以每次循环排除掉 k/2 个数。这里需要注意,中位数当奇数时只需要找一个数字,偶数时找两个数字,为了统一形式,这里都找两个,只不过奇数时两个一样罢了。

无论是找第奇数个数字,还是第偶数个数字,对我们的算法并没有影响,而且在算法进行中,k 的值都有可能从奇数变为偶数,最终都会变为 1 或者由于一个数组空了,直接返回结果。

所以我们采用递归的思路,为了防止数组长度小于 k/2,所以每次比较 min(k/2,len(数组) 对应的数字,把小的那个对应的数组的数字排除,将两个新数组进入递归,并且 k 要减去排除的数字的个数。**递归出口就是当 k=1 或者其中一个数字长度是 0 了。**看到这可能还是有点迷,下面看代码,上面有注释。

代码

	// 转化为 求第k小个数字
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n = nums1.length;
        int m = nums2.length;
        // 奇数K相同
        int left = (m + n + 1) / 2;
        int right = (m + n + 2) / 2;
        // 递归调用函数,求两个数组的第K小的元素
        return (getKth(nums1,0,n-1,nums2,0,m-1,left) + getKth(nums1,0,n-1,nums2,0,m-1,right)) / 2.0;  
    }

    // 求第K小的元素
    public int getKth(int[] nums1,int start1,int end1,int[] nums2,int start2,int end2,int k){
        // 数组1的长度
        int len1 = end1 - start1 + 1;
        // 数组2的长度
        int len2 = end2 - start2 + 1;
        //为了方便计算,我们这里规定len1小于len2,这样就能保证如果有数组空了,一定是 len1 
        if(len1 > len2) return getKth(nums2,start2,end2,nums1,start1,end1,k);
        if(len1 == 0) return nums2[start2 + k - 1]; // 索引从0开始,要减1
        // 只剩一个元素了,选两个数组最小的即可
        if(k == 1) return Math.min(nums1[start1],nums2[start2]);

        // 为了防止数组不够用,因此如果超了直接赋值给最后一位
        int i = start1 + Math.min(k /2, len1) - 1;
        int j = start2 + Math.min(k / 2,len2) - 1;

        // 比较当前元素,把小的那一部分截取掉
        if(nums1[i] > nums2[j]){
            // 截取nums2
            return getKth(nums1,start1,end1,nums2,j + 1,end2,k - (j - start2 + 1));
        }else{
             // 截取nums1
            return getKth(nums1,i + 1,end1,nums2,start2,end2,k - (i - start1 + 1));
        }
    }

总结

二分的题目比较灵活,因此要注意其变形,关键还在于如何确定找第几个,通过二分缩小范围,以及退出二分的条件

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值