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

给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数。
进阶:你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

方法1:时间复杂度 O ( l o g ( m + n ) ) O(log(m+n)) O(log(m+n))

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;
        return (findKth(nums1, 0, nums2, 0, left) + findKth(nums1, 0, nums2, 0, right)) / 2.0;
    }
    //i: nums1的起始位置 j: nums2的起始位置
    public int findKth(int[] nums1, int i, int[] nums2, int j, int k){
        if( i >= nums1.length) return nums2[j + k - 1];//nums1为空数组
        if( j >= nums2.length) return nums1[i + k - 1];//nums2为空数组
        if(k == 1){
            return Math.min(nums1[i], nums2[j]);
        }
        int midVal1 = (i + k / 2 - 1 < nums1.length) ? nums1[i + k / 2 - 1] : Integer.MAX_VALUE;
        int midVal2 = (j + k / 2 - 1 < nums2.length) ? nums2[j + k / 2 - 1] : Integer.MAX_VALUE;
        if(midVal1 < midVal2){
            return findKth(nums1, i + k / 2, nums2, j , k - k / 2);
        }else{
            return findKth(nums1, i, nums2, j + k / 2 , k - k / 2);
        }        
    }
}

方法2:时间复杂度 O ( l o g ( m i n ( m + n ) ) ) O(log(min(m+n))) O(log(min(m+n)))
本题是典型的分治和二分的题目,对于两个数组长度分别为m和n的数组,本题最优的解法是 O ( l o g ( m i n ( m , n ) ) ) O(log(min(m,n))) O(log(min(m,n)))。求中位数问题一般转化为topK问题,对于中位数而言就是一个有序数组的中间的数,所以既可以看做是第k大也可以看做是第k小的元素,此处的k=(m+n+1)/2,注意当 (m+n)为奇数的时候,k就是等于(m+n+1)/2,但是当 (m+n) 为偶数的时候,所求的中位数即第k大的元素的k其实是k1 = (m+n)/2, 和k2 =(m+n)/2 + 1两个数的均值,先不考虑偶数时候的k2 = (m+n)/2 + 1, 我们可以看到当(m + n)等于偶数的时候,k1 = (m+n/2 =(m+n+1)/2, 所以为了和奇数时保持一致当(m + n)等于偶数的时候,取 k1 = (m+n+1)/2。

主要的思想就是在两个数组上进行寻找切割点cut1, cut2,由于两个数组的长度一定,且k一定,因此为了达到一个较短的时间复杂度,只需要在较短的数组上进行寻找到一个合适的切割点,然后在数组2上的切割点也就相应的确定了,因为数组1的切割点左边的元素的个数加上数组2的切割点左边的元素的个数等于k。那么针对数组1来说,合适的位置在哪?合适的位置满足:数组1的左半部分的最大值要小于数组2的右半部分的最小值且数组1的右半部分的最小值要大于数组2的左半部分的最大值。如下图midA-1就是数组1的左半部分的最大值下标(无论数组1的长度是奇数还是偶数,midA-1都是数组1的左半部分的最大值),midB就是数组2的右半部分的最小值下标(无论数组2的长度是奇数还是偶数,midB都是数组2的右半部分的最小值)。
在这里插入图片描述

本题的代码主要有以下的难点:

  • 边界的处理,即左边最大右边最小的索引的求取。
  • 二分查找的r的选取和while循环的是否加等号,由于是属于解不存在的二分且l和r的更新为mid+1, mid -1,因此r为长度,while取等号,在该种情况下,解cut1不存在的情况分为为0,和len(nums1)。
  • 最终观察代码的实现,可以发现代码在某种程度上具有对称性
  • 注意最后要将结果强制转换为double类型。
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        //选择一个较短的数组进行遍历
        int len1 = nums1.length;
        int len2 = nums2.length;
        if (len1 > len2) {
            return findMedianSortedArrays(nums2, nums1);
        }
        int l = 0;
        int r = len1;//因为存在在nums1中找不到解的情况(即nums1中的元素都比较小),所以右指针为len1而不是len1-1
        int k = (len1 + len2 + 1)/2;//将中位数问题转化为求第k大的问题
        int cut1 = 0;
        int cut2 = 0;
        while (l<=r) {//因为存在在nums1中找不到解的情况, 所以此处的二分while循环为等号
            cut1 = l + (r-l)/2;
            cut2 = k - cut1;
            if (cut1 > 0 && nums1[cut1-1]>nums2[cut2]) {
                r = cut1 - 1;
            } else if (cut1 <len1 && nums1[cut1]<nums2[cut2-1]){
                l = cut1 + 1;
            } else {
                break;
            }
        }
        //下面就是求的left_ans和right_ans
        int left_ans = 0;
        int right_ans = 0;
        if (cut1 == 0) {
            left_ans = nums2[cut2-1];
        } else if (cut2 == 0) {
            left_ans = nums1[cut1-1];
        } else {//cut1解存在
            left_ans = Math.max(nums1[cut1-1], nums2[cut2-1]);
        }
        if ((len1 + len2)%2==1) return (double)left_ans;//注意要转化为double
        if (cut1 == len1) {
            right_ans = nums2[cut2];
        } else if (cut2 == len2) {
            right_ans = nums1[cut1];
        } else {
            right_ans = Math.min(nums1[cut1], nums2[cut2]);
        }
        return (double)(left_ans + right_ans)/2;//注意要转化为double

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值