4. 两个排序数组的中位数

知乎ID: 码蹄疾
码蹄疾,毕业于哈尔滨工业大学。
小米广告第三代广告引擎的设计者、开发者;
负责小米应用商店、日历、开屏广告业务线研发;
主导小米广告引擎多个模块重构;
关注推荐、搜索、广告领域相关知识;

题目

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 。
请找出这两个有序数组的中位数。要求算法的时间复杂度为 O(log (m+n)) 。
示例 1:

nums1 = [1, 3]
nums2 = [2]
中位数是 2.0

示例 2:

nums1 = [1, 2]
nums2 = [3, 4]
中位数是 (2 + 3)/2 = 2.5

分析

这个题主要是事件复杂度的问题,归并的做法就不用想了,o(m+n)的时间复杂度。看到log级别的肯定想到二分查找了,关键是怎么划分的问题,这也是本体的难点所在,校招可能会考那么难的题,社招这种题我肯定不会哪来面候选人,我自己现场也不一定做得出来。
首先,看中位数是啥?

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

首先,让我们在任一位置 i 将 A 划分成两个部分:

                left_A   |  right_A      
A[0], A[1], ..., A[i-1]  |  A[i], A[i+1], ..., A[m-1]

类似,

                     left_B  |  right_B      
    B[0], B[1], ..., B[j-1]  |  B[j], B[j+1], ..., B[n-1]

我们把A,B两个数组合并一下:

          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]

按照中位数的定义,问题求解变成:
1.len(leftPart)=len(rightPart)
2.max(leftPart) ≤ min(rightPart)
3.median = [max(leftPart) + min(rightPart)] / 2
这个还是不那么好求解,我们还是不知道怎么去在规定时间复杂度时间内搜索出max(leftPart) 和 min(rightPart)
,这需要一下找一下i和j之间的关系。为了简化分析,我们先不考虑 i=0,i=m,j=0,j=n 这样的临界条件。
1. len(leftPart)=len(rightPart)条件转化为:
i+j=m−i+n−j+1
i+j=(m+n+1)/2
j = (m+n+1)/2 - i
看一下约束条件:
但是j不能是负数
(m+n+1)/2 > i
i的取值范围是[0,m]
(m+n+1)/2 > m
n+1 > m
所以,n ≥ m才能使用上面的关系.

2.max(leftPart)≤min(rightPart)条件转化为:
B[j−1]≤A[i] 且 A[i-1] ≤ B[j]

3.条件三分一下奇数和偶数:
a. 当 m + n为奇数时,中位数为:max(A[i−1],B[j−1])
​​b. 当 m + n为偶数时,中位数为:max(A[i−1],B[j−1])+min(A[i],B[j]) / 2

下面开始二分查找:

1.设 imin = 0,mimax=m, 然后开始在 [imin,imax] 中进行搜索。
2. i = (imin + imax) / 2, j = (m+n+1) / 2 -i
3.开始搜索上面的第二个条件:
      a. B[j−1]≤A[i] 且 A[i−1]≤B[j]:
          找到目标对象
      b. B[j−1]>A[i]:
          这意味着A[i] 太小,我们必须调整 i 以使B[j−1]≤A[i]
          怎么调整? 增大i,在[i+1,imax]区域二分
          (1)增大i,j变小. B[j−1]会减小,A[i]会增大,可以向解空间靠近一步.
          (2)减小i,j变大. B[j−1]会变大,A[i]会减小,不能这么搜索.
      c. A[i−1]>B[j]:
          类似b,减小i,在[imin,i−1]区域二分  

主算法完成了,来考虑边界问题吧。
i=0,i=m,j=0,j=n,此时A[i−1],B[j−1],A[i],B[j] 可能不存在。
我们需要做的是确保max(leftPart)≤min(rightPart)。 如果A[i−1],B[j−1],A[i],B[j] 中部分不存在,那么我们只需要检查这两个条件中的一个(或不需要检查)。

code

public class Solution {
    public double findMedianSortedArrays(int[] A, int[] B) {
        int m = A.length;
        int n = B.length;
        if (m > n) { // 交换,保证 m<=n
            int[] temp = A;
            A = B;
            B = temp;
            int tmp = m;
            m = n;
            n = tmp;
        }
        int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
        while (iMin <= iMax) {
            int i = (iMin + iMax) / 2;
            int j = halfLen - i;
            if (i < iMax && B[j - 1] > A[i]) {
                iMin = iMin + 1; // i 太小
            } else if (i > iMin && A[i - 1] > B[j]) {
                iMax = iMax - 1; // i 太大
            } else { // i 命中目标,分奇数和偶数.
                int maxLeft = 0;
                if (i == 0) {
                    maxLeft = B[j - 1];
                } else if (j == 0) {
                    maxLeft = A[i - 1];
                } else {
                    maxLeft = Math.max(A[i - 1], B[j - 1]);
                }
                if ((m + n) % 2 == 1) {
                    return maxLeft;
                }

                int minRight = 0;
                if (i == m) {
                    minRight = B[j];
                } else if (j == n) {
                    minRight = A[i];
                } else {
                    minRight = Math.min(B[j], A[i]);
                }

                return (maxLeft + minRight) / 2.0;
            }
        }
        return 0.0;
    }
}

这里写图片描述

猜你喜欢

转载自blog.csdn.net/creazierHIT/article/details/80865539