力扣-寻找两个正序数组的中位数

题目:

给定两个大小为 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

二分解决方法

题目要求将时间复杂度降低到O(log(m+n)),如果对时间复杂度的要求有log,通常都需要用到二分查找,这道题也是通过二分查找实现

根据中位数的定义,当m+n是奇数的时候,中位数是两个有序数组中的第(m+n)/2个元素,当m+n是偶数的时候,中位数是两个有序数组中的第(m+n)/2和第(m+n)/2+1个元素的平均值。因此,这道题可以转换为寻找两个有序数组中的第k小的数,其中k为(m+n)/2或者(m+n)/2+1
假设有序数组分别是A和B要找到第k个元素,我们可以比较A[k/2-1]和B[k/2-1],其中/标识除法符号。由于A[k/2-1]和B[k/2-1]的前面分别有A[0 … k/2-2]和B[0 … k/2-2],即k/2-1个元素,对于A[k/2-1]和B[k/2-1]中的较小值,最多只有(k/2-1)+(k/2-1) <=k-2个元素比他小,那么他就不是第k个小的数
因此我们可以归纳出三种情况

  • 如果A[k/2-1]<B[k/2-1],则比A[k/2-1]小的数最多只有k-2个,因此A[k/2-1]不可能是第k个数,A[0]到A[k/2-1]也不可能都不可能是第k个数,可以全部排除
  • 如果A[k/2-1]>B[k/2-1],则可以排除B[0 …k/2-1]
  • 如果A[k/2-1]=B[A/2-1],则可以归入第一种情况处理

可以看到,比较A[k/2-1]和B[k/2-1]之后可以排除k/2个可能是第k小的数,查找范围小了一半.同时,我们将在排除后的新数组上继续进行二分查找,并且根据我们排除的个数,减小k的值,这是因为我们排除的数都是不大于第k小的数字

有以下三种情况需要特殊处理

  • 如果A[k/2-1]或者B[k/2-1]越界,那么我们可以选取对应数组中最后的一个元素.在这种情况下,我们必须根据排除数的个数减少k的值,而不能直接将k减去k/2

  • 如果一个数组是空,说明该数组中的所有元素都被排除,我们可以直接返回另一个数组中第K小的元素.(当前表头索引+k)

  • 如果k=1,我们只需要返回数组首元素的最小值即可

  • 实现代码

package 题库.寻找两个正序数组中位数;

public class Solution {

    public static void main(String[] args) {
        int []num1={1,2,3,4};
        int []num2={2,3,4,5};
        Solution solution = new Solution();
        System.out.println(solution.findMedianSortedArrays(num1,num2));
    }

    /**
     * 在两个正向数组中获得中位数
     * @param num1
     * @param num2
     * @return
     */
    private double findMedianSortedArrays(int[] num1, int[] num2) {
        //获取两个数组的长度
        int alen = num1.length, blen = num2.length;
        //获取两个数组总的长度
        int tlen = alen+blen;
        //如果是奇数
        if (tlen % 2 == 1){
            return BinarySearch(num1,num2,tlen/2+1);
        }else{
            //如果是偶数
            return (BinarySearch(num1,num2,tlen/2+1)+BinarySearch(num1,num2,tlen/2+1))/2.0;
        }
    }

    /**
     * 二分查找法
     * @param a
     * @param b
     * @param k 需要查找的第k个元素
     * @return
     */
    public double BinarySearch(int []a, int []b, int k){
        int al = a.length, bl = b.length;
        int ai =0, bi = 0;
        int nai = 0, nbi = 0;
        //只要还能继续二分查找
        while (true){
            //三种特殊情况
            //a数组索引等于a数组的长度
            if (ai == al){
                return b[bi+k-1];
            }
            //b数组索引等于b数组的长度
            if (bi == bl){
                return a[ai+k-1];
            }
            //第k个字符是不是第一个
            if (k == 1){
                return Math.min(a[ai],b[bi]);
            }
            //常规操作,获取第k/2个元素的索引或者如果超出界限获取该数组最后一个元素
            nai = Math.min(ai+k/2, al)-1;
            nbi = Math.min(bi+k/2, bl)-1;
            //两种常规情况
            //该索引a数组大于b数组
            if (a[nai] >= b[nbi]){
                k-= nbi-bi+1;
                bi = nbi+1;
            }else{
                //b数组大于a数组
                k-=nai-ai+1;
                ai = nai+1;
            }
        }
    }
}

©️2020 CSDN 皮肤主题: 黑客帝国 设计师:上身试试 返回首页