LeetCode 第4题:两个排序数组的中位数

LeetCode 第4题

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

解题思路

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

我把中位数理解成一道线,它将示例1中两个有序数组重组分成了 [1,2], [2,3]两个子集。

假设我们把给定的两个有序集合进行按照中位数的定义进行划分成两个符合要求的子集Left[],Right[]。每个子集长度为 (m+n)/2或者 m+n + 1)/2,这两个子集的元素都是由 nums1 和 nums2 的一些元素组成。                                                                            

为了方便解释,假设 A =nums1[m],B = nums2[n],其中在Left子集中,A有i个元素,B有j个元素,则可以推测出下面

        Left                 |        Right
    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]

由于我们按照中位数定义进行的推测。可以得出以下两个公式

  • len(Left)  =  len(Right)
  • max(Left)  <=  min(Right)

此时 中位数 = ( max(Left) + min(Right) ) / 2;

从这两个公式我们可以推出  i + j = m - i + n - j (或者 m - i + n -j + 1) ;    ==》 if n >= m, i < m; j = (m+n +1) / 2 - i;

解释一下为什么 j = (m+n +1) / 2 - i;当 m + n 为奇数,为了符合定义,

  • len(Left)  =  len(Right) , 需要在集合长度为 min(m,n)中添加一个中位数,即 总长度+1,此时j的数字才不会出错
  • 当 m + n 为偶数时, j = (m + n + 1) / 2  - i,数值不会变化

A[i-1] <= B[j] 以及 B[j - 1] <= A[i]

这里说一句,为了确保 j 不小于0, n 必须不小于m才可以。

这里根据公式开始二叉树递归查找:

假设 imin = 0 , imax = m; 在[ imin , imax]中进行搜索

由于A,B都是有序数组,令 i = ( imin + imax) / 2 ,  j = (m+n +1) / 2 - i;

由于 len(Left)  =  len(Right),则我们会遇到三种情况

当 A[i-1] <= B[j] 以及 B[j - 1] <= A[i] 时, 我们找到了i,问题得解;

当 A[i-1] > B[j] 时,由于 j = (m+n +1) / 2 - i;说明i过大,需要 i--;

当 B[ j - 1 ] > A[i] 时,说明 i 过小,需要i++;

Java Code:

class Solution {
    public double findMedianSortedArrays(int[] A, int[] B) {
        int m = A.length;
        int n = B.length;
        if (m > n) { // to ensure 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 is too small
            }
            else if (i > iMin && A[i-1] > B[j]) {
                iMax = iMax - 1; // i is too big
            }
            else { // i is perfect
                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;
    }
}

你可能感兴趣的:(LeetCode)