MedianOfTwoSortedArrays

来源:互联网 发布:软件工程学什么 编辑:程序博客网 时间:2024/06/06 13:07

git

package com.leetcode;/** * Created by RayDu on 2017/9/2. */public class  MedianOfTwoSortedArrays {    /**     * 17/9/22     * 解题思路:     *     * 这道题的时间复杂度要求是:O(log(m+n)),直觉上觉得使用类似于二分查找的方式来做效果应该不错,对于我自己的难点在于     * 1.对于中点是第多少大的数、下标应该是多少     * 2.二分查找的终止条件是什么     * 3.有什么边界条件需要考虑     *     * 一、整体思路     *  比如:     *     a :a[0]=1 a[1]=2 a[2]=3 a[3]=4 a[4]=5 a[5]=6 a[6]=7     *     b :b[0]=2 b[1]=4 b[2]=5 b[3]=7 b[4]=8 b[5]=9 b[6]=10     * 这里我们知道,     * 如果 a[3] < b[3],我们可以说max(a[3],b[3])是两组数列左边最大的数,如果这个值小于min(a[4],b[4])     * 那么max(a[3],b[3])就是整组数(a、b)中第6个数。     * 所以     * 1.我们需要找一个约束条件,比如 有a[i],b[j] i+j = k (常数) 这样我们改变i的值,j就可以根据约束条件改变了     * 2.如何保证 max(a[i],b[j])就是第k个数呢? 只需保证在满足1的约束条件下 max(a[i],b[j]) < min(a[i+1],b[j+1]) 当然这里肯定有边界条件需要考虑     * 3.然后就是如何搜索的问题了,其实就是找在约束条件1下,满足条件2的组合,这里的停止条件自然是:要么搜索到最后一个了,要么条件2被满足~     *     * 二、下标问题     * 前面一直在说第k大的数,其实困扰最久的问题是约束条件的构造:median of xxx ,到底是第k(k=?)的数呢?     * 这是一个蛮关健的约束条件     *     * 假设一个数组长 m     * 中间的那个数的下标是(从0起): m/2 (m-1)/2 --> 也就是说"一半"有: m/2个     * eg 对于5:0,1,[2],3,4   5/2 = 2[下取整]  4/2 = 2     *    对于8: 0,1,2,[3],[4],5,6,7 8/2 = 4 7/2 = 3     *     * 对于这道题,假设两个数组是m,n长度的数组, 那么中间的数下标是 (m+n)/2,(m+n-1)/2 他们所包含的子数组长度是 (m+n+2)/2 (m+n+1)/2     * 这里我们其实只用讨论其中一个长度即可,假设我们讨论(m+n+1)/2,假设我们有一个无形的分割线     * a[0] ... a[i] | a[i+1] .... a[m-1]     * b[0] ... b[j] | b[j+1] .... b[n-1]     * 如果说那么左侧的个数是: i + 1 + i + 1 = (m+n+1)/2     * 我们知道max(a[i],a[j])就是第(m+n+1)/2 大的数     * 边界情况: i = -1/j = -1 ->  整个a/b数组都在大的一侧, 此时 b[j]/a[i] 是第k个数     *          i = m-1/j = n-1 -> 整个数组都在小的一侧,若需要求(m+n+2)/2 则是b[j+1]/a[i+1]     *     * 上面我们其实是在看小的那部分的max,同样的我们可以关注大的那部分的min,那么我们可以换一个视角来看:     * a[0] ... a[i-1] | a[i] .... a[m-1]     * b[0] ... b[j-1] | b[j] .... b[n-1]     * 同样的,左侧个数是 (i-1)+1 + (i-1)+1 = (m+n+1)/2     * 边界情况变成了i = 0/j = 0 以及 i=m/j=n     *     * 三、二分查找的思路:     *     * 初始: min = -1,max = s1.length-1     * 迭代:     *  i = (min+max)/2     *  j = (m+n+1)/2 - 2 - i     *     * 我们希望有 max(s1[i],s2[j]) <= min(s1[i-1],s2[j-2])     * 事实上,不一定遂人愿     * 如果说: s1[i]>s2[j+1] 则说明i需要减小,所有i以后的都不可以     * 如果说: s1[i+1]<s2[j] 则说明i需要增大,所有i以前的都不可以     * 如果说: s1[i]<=s2[j+1] && s2[j]<=s1[i+1],则满足条件     *     * 四、最后说一个小技巧     * 如果我们二分的字符串是长的那个,可能就会出现短的那个下标越界的情况,为了避免这个情况,我们可以在一开始就     * 做一些操作,确保是用的短的数组做二分     *     */    public static double findMedianSortedArrays(int[] nums1, int[] nums2) {        int[] tmp;        if(nums1.length > nums2.length){            tmp = nums1;            nums1 = nums2;            nums2 = tmp;        }        //注意这里不能直接-3,因为会有整数的精度问题        int half = (nums1.length + nums2.length + 1)/2;        int min = -1;        int max = nums1.length - 1;        while (min<=max){            int i = (min + max) / 2;            int j = half - 2 - i ;            if( i<max && nums1[i+1] < nums2[j]){                //这里有一个问题,按理说 min = i + 1 更快?可实时不是这样的                min = min + 1;            }else if(i > min && nums1[i] > nums2[j+1]){                max = max - 1;            }else {                int left = 0;                if(i == -1)left = nums2[j];                else if(j == -1)left = nums1[i];                else left = Math.max(nums1[i],nums2[j]);                if((nums1.length+nums2.length)%2 == 1)return left;                int right = 0;                if(i==nums1.length - 1)right=nums2[j+1];                else if(j==nums2.length - 1)right=nums1[i+1];                else right=Math.min(nums1[i+1],nums2[j+1]);                return (right+left)/2.0;            }        }        return 0.0;    }    public static void main(String[] args){        int[] a ={3,4,5,8,10};        int[] b ={1,2,6,7,9};        System.out.print(findMedianSortedArrays(a,b));    }}