寻找两个有序数组的中位数

 方法一:合并两个数组,然后求中位数,时间复杂度为:O(m+n)

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
       int length1 = nums1.length;
        int length2 = nums2.length;
        int[] result = new int[length1 + length2];
        int i = 0,j = 0,k = 0;
        while (i < length1 && j < length2){
            if (nums1[i] < nums2[j]){
                result[k] = nums1[i];
                i++;
            }else {
                result[k] = nums2[j];
                j++;
            }
            k++;
        }
        while (i < length1){
            result[k] = nums1[i];
            k++;
            i++;
        }
        while (j < length2){
            result[k] = nums2[j];
            k++;
            j++;
        }
        if (k % 2 == 0){
            return (result[k/2] + result[k/2 - 1]) / 2.0;
        }else {
            return result[k/2];
        }
    }
}

方法二:分治法

一、中位数的作用

将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。

二、双数组划分

对数组A、B分别在\large ij处进行划分,并且将A和B的左半部分放入到left_part集合当中,把A和B的右半部分放入到right_part集合当中。

          left_part          |        right_part
    A[0], A[1], ..., A[i-1]  |  A[i], A[i+1], ..., A[m-1]
    B[0], B[1], ..., B[j-1]  |  B[j], B[j+1], ..., B[n-1]

 根据上边的划分可以推出:

\large len(\text{left\_part}) = len(\text{right\_part})               \large m+n是偶数

\large len(left\_part ) > len(right\_part )            \large m+n是奇数  (左边比右边多一个数)

\large \max(\text{left\_part}) \leq \min(\text{right\_part})          

那么中位数也就可以得到:

\large median=\frac{\max(\text{left\_part}) +min(\text{right\_part})}{2}                 \large m+n是偶数

\large median=max(left\_part )                                                       \large m+n是奇数

要想得到上边的结论,条件有两个:

\large i+j=m-i+n-j(或者:\large i+j=m-i+n-j+1

如果\large n\geqslant m,只需要\large i=[0,m]\large j=\frac{m+n+1}{2}-i

\large B[j-1]\leqslant A[i]并且\large A[i-1]\leqslant B[j]

为了简化分析:假设\large A[i-1],B[j-1],A[i],B[i]总是存在的;具体情况放在后边讨论。

为什么要求\large n\geqslant m成立? 是为了确保j的值不为负数。

三、明确需求

说了这么多,我们需要做的就是在\large [0,m]中找到目标对象\large i,使得以下条件成立:

\large B[j-1]\leqslant A[i]并且\large A[i-1]\leqslant B[j],其中\large j=\frac{m+n+1}{2}-i

四、怎么去找

采用二分查找。

1.设 \large \text{imin} = 0,\text{imax} = m,然后开始在\large [imin,imax]中进行搜索。

2.令\large i=\frac{imin+imax}{2},j=\frac{m+n+1}{2}-i

3.现在\large len(\text{left\_part}) = len(\text{right\_part});那么有三种情况需要说明:

  • \large B[j-1]\leqslant A[i]并且\large A[i-1]\leqslant B[j],这个条件就是搜索结束的标记。
  • \large B[j-1]> A[i],说明\large A[i]划分的太小了,需要把\large i向后移动,以便增大\large A[i];所以新的搜索范围就变为:\large [i+1,imax]
  • \large A[i-1]>B[j],说明\large A[i]划分的太大了,需要把i向前移动,以便减小\large A[i];所以新的搜索范围就变为:\large [imin,i-1]

4.当搜索范围改变时,继续循环判断即可。

当找到目标对象\large i时,中位数为:

\large max(A[i-1],B[j-1]),当\large m+n为奇数的时候

\large \frac{max(A[i-1],B[j-1])+min(A[i],B[j])}{2},当\large m+n为偶数的时候

 五、处理边界值

\large i=0,i=m,j=0,j=n,此时\large A[i-1],B[j-1],A[i],B[i]可能发生越界。但是通过分析可以得到,只要\large i不越界,\large j就不会越界。

越界就是发生在重新确定\large imax\large imin的时候,就是在\large i-1或者\large i+1之前判断一下当前\large i是否在\large [0,m]内。

六、代码

class Solution {
   public  double findMedianSortedArrays(int[] nums1, int[] nums2){
        int m = nums1.length;
        int n = nums2.length;
        //一定要保证n≥m成立
        if (m > n){
            //交换两个数组
            int[] temp = nums1;
            nums1 = nums2;
            nums2=temp;

            //交换m和n
            int temp2 = m;
            m = n;
            n = temp2;
        }

        int imin = 0,imax = m,half = (m + n + 1) / 2;
        while (imin <= imax){
            int i = (imin + imax) / 2;
            int j = half - i;
            if (i > imin && nums1[i-1] > nums2[j]){
                //nums1[i]太大了,需要小一点
                imax = i - 1;
            }else if (i < imax && nums2[j-1] > nums1[i]){
                //nums1[i]太小了,需要大一点
                imin = i + 1;
            }else {
                //nums1[i]符合划分

                //找左边的最大值
                int leftMax = 0;
                if (i == 0){
                    leftMax = nums2[j-1];
                }else if (j == 0){
                    leftMax = nums1[i-1];
                }else {
                    leftMax = Math.max(nums1[i-1], nums2[j-1]);
                }
                if ((m + n) % 2 == 1){
                    return leftMax;
                }
                //找右边的最小值
                int rightMin = 0;
                if (i == m){
                    rightMin = nums2[j];
                }else if (j == n){
                    rightMin = nums1[i];
                }else {
                    rightMin = Math.min(nums1[i],nums2[j]);
                }
                return (leftMax + rightMin) / 2.0;
            }
        }
        return 0.0;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值