leetCode4两个有序数组的中位数

197 阅读3分钟

描述:给定两个有序的数组(长度分别为m和n)求其中位数,要求算法的事件复杂度为O(log)(m+n)

如果不设定时间复杂度的话,直接将两个数组合并成一个,而后排序,求值就好,但是,设定了log的时间复杂度,那就一定是用到二分法来进行处理。

思路:
假设 A数组 长度为m,B数组长度为n;
我们在A数组中从i的位置将A数组分为两部分。A[0]--A[i-1]作为leftA 和A[i]--A[m]作为rightA(这里我们假定A[m]是有值的)
同样的,B也如此操作(以j为分割点)。得到leftB,rightB
最终我们要满足的是
leftA+leftB == rightA+rightB 或者是 leftA+leftB +1 = rightA+rightB
左边等于右边(偶数情况),或者是左边比右边多一个(奇数情况)。
而且,A[i-1]<B[j],B[j-1]<A[i]

如此一来,我们便可以得到中位数一定是在
(Math.max(leftA,leftB) + Math.min(rightA,rightB))/2
或者是 Math.max(leftA,leftB)
当然这是在两个数组的长度之和是偶数的情况下。


i 和 j

i 与 j的关系可以看做是 i+j = m-i +n-j
j = (m+n)/2-i
i 表示数组m的切割点,如果两个数组之和是偶数当然可以做到这样,但是,如果两个数组之和是奇数的话我们就要变化一下:
j = (m+n+1)/2-i #注意:这里的(m+n+1)/2表示的是求整数商,也就是抛弃小数点,这样我们就兼容了奇数或者是偶数的情况。
如果,m+n是偶数的话,比如是3和5,那
(3+5)/2==4
而
(3+5+1)/2==4.5==>4
如此是不影响切割的位置的。

但是,如果和是奇数的话,
比如m为3,n为4,那经过加1之后切割点就变成4,那么最终定然会形成左边比右边多一个的情况。
比如
A=【1,2,3】长度为m
B=【1,2,3,4】 长度为n
i=2的时候,j= (m+n+1)/2-i = 2
左边为left || 右边为 right
【1,2】      【3】
【1,2】      【3,4】

如此根据定理:
如上的切割满足:
A[i-1]<B[j]
B[j-1]<A[i]
并且left.length=right.length或者是left.length = right.length +1

如此便可以得到中位数是left中最大的一个值 也就是2
如果是偶数的情况,比如
A=【1,2,3,4】长度为m
B=【1,2,3,4】 长度为n
i=2的时候,j= (m+n+1)/2-i = 2
左边为left || 右边为 right
【1,2】      【3,4】
【1,2】      【3,4】

如此那么中位数肯定是左边的最大值和右边的最小值之间求平均数了。

注意

因为 i+j = m-i +n-j
j = (m+n)/2-i
if m>n的话,比如m = 5,n = 4
如果当i = 5(i验证长度为m的数组进行不断的试探)的时候,j就变成负数了 -1
所以,参照的数组一定要以长度较小的来当做值。

代码实现

var findMedianSortedArrays = function(nums1, nums2) {
    var m;
    var n;
    
    if(nums1.length>nums2.length){
        n=nums1
        m=nums2
    }else{
        m=nums1
        n=nums2
    }
    var imin = 0,imax=m.length,lenHalf= Math.floor((m.length+n.length+1)/2);
    while(imin<=imax){
        var i = Math.floor((imin+imax)/2)
        var j = lenHalf - i
        
        if(i>imin && m[i-1]>n[j]){
            imax=i-1;
        }else if(m[i]<n[j-1] && i<imax){
            imin=i+1;
        }else{
            var maxLeft = 0
            if(i==0){
                maxLeft = n[j-1]
            }else if(j==0){
                maxLeft = m[i-1]
            }else{
                maxLeft = Math.max(n[j-1],m[i-1])
            }
            if((m.length+ n.length) % 2 ==1){
                return maxLeft
            }
            var minRight = 0;
            if(i==m.length){
                minRight = n[j]
            }else if(j==n.length){
                minRight = m[i]
            }else{
                minRight = Math.min(n[j],m[i])
            }
            return (maxLeft+minRight)/2
        }
        
    }
    return 0.0
};