LeetCode 4. Median of Two Sorted Arrays

199 阅读4分钟

4. Median of Two Sorted Arrays

Question

There are two sorted arrays nums1 and nums2 of size m and n respectively.

Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

You may assume nums1 and nums2 cannot be both empty.

Example 1:

nums1 = [1, 3]
nums2 = [2]

The median is 2.0

Example 2:

nums1 = [1, 2]
nums2 = [3, 4]

The median is (2 + 3)/2 = 2.5

Solution

该问题的主要难点在于O(log(m+n))的时间复杂度限制,这意味着无法将2个有序数组合并排序之后再获取中位数。

解法:在不合并2个有序数组的情况下,使用Binary Search找出中位数。

Binary Search似乎很难在2个数组上找出中位数,故需要进一步挖掘中位数的性质:

  • 对于长度为偶数E的有序数组,将其从中间分为2个长度为E/2的子数组Left与Right,Median = (MaxLeft+MinRight)/2
  • 对于长度为奇数O的有序数组,将其从中间分为长度为(O+1)/2的子数组Left和长度为(O-1)/2的子数组Right,Median = MaxLeft
  • MaxLeft是左子数组(也是较小值构成的数组)中的最大值(也是最右端的值);MinLeft是右子数组(较大值构成的数组)中的最小值(也是最左端的值)

对于作为输入的2个有序数组A, B来说,合并数组的左子数组一定由A的前i个元素和B的前j个元素构成。因为他们代表了输入中的较小部分元素。确定了i, j的值就可以得到需要的中位数。

推理过程:

MaxLeft <= MinRight; MaxLeft = max(A[i-1], B[j-1]); MinRight = min(A[i], B[j]); i+j = int((m+n+1)/2)

等价于:max(A[i-1], B[j-1]) <= min(A[i], B[j]); i+j = int((m+n+1)/2)

等价于:

A[i-1]<=A[i];

A[i-1]<=B[j]; (1)

B[j-1]<=B[j];

B[j-1]<=A[i]; (2)

i+j=int((m+n+1)/2); (3)

其中除(1) (2) (3)式之外的2个不等式在题设条件(A, B是有序数组)下是恒成立的,可以省略。

那么本题就相当于使用Binary Search解决下列的一个规划问题,得到一组必定存在的i, j

Limitation:

  • A[i-1]<=B[j]; (1)
  • B[j-1]<=A[i]; (2)
  • i+j=int((m+n+1)/2); (3)

为什么要说“规划问题”?因为如果把A, B看做两个离散空间上的函数,与中学数学中的线性规划问题很相似,区别就是线性规划用推理方式寻找解的区域,该问题用搜索方法求解。

由于(3)等式规定了i, j的恒定关系,故只有i一个变量。找到满足(1)(2)式的i即解决该问题。

对于给定的i,(1)(2)共有四种成立状态的组合:

  1. (1)(2)均成立。很显然这就找到满足要求的i了。
  2. (1)成立 (2)不成立。i需要增大,增大的方式当然不是简单的加一,而是要利用Binary Search咯。
  3. (1)不成立 (2)成立。i需要减小,同理。
  4. (1)不成立 (2)不成立。利用A, B有序数组的性质可以证明矛盾,该情况不可能存在。

实现注意要点:

  • corner cases,i增大减小都需要验证一下是否有数组空间做这个操作。如果遇到(1)(2)无法满足,数组空间又不足怎么办?由于这个问题必定有解,那肯定就是搜索下界等于搜索上界,i也随之确定。
  • 空数组输入问题。“You may assume nums1 and nums2 cannot be both empty.”意味着可能包含一个空数组。
  • 为什么要先假设m<n?因为j=(m+n+1)/2-i,而0<= i <=m,如果m较大那么i的实际可取值范围应该在0到一个比m小的正数之间,从而才能使j始终为正数。为了使算法简洁,不如假设m<n,如果不符合就交换2个参数。
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size();
        int n = nums2.size();
        if (m>n) {
            return findMedianSortedArrays(nums2, nums1);
        }
				
      	// iMin, iMax确定当前Binary Search的范围
      	// 注意
        int iMin=0, iMax=m, leftLen = (m+n+1)/2;
        while(iMin<=iMax) {
            int i = (iMin+iMax)/2;
            int j = leftLen - i;
            if (i<iMax && nums2[j-1]>nums1[i]) {
                iMin = i+1;
            } else if (i>iMin && nums1[i-1]>nums2[j]) {
                iMax = i-1;
            } else {
                int maxLeft = 0, minRight = 0;
                if (i==0) maxLeft = nums2[j-1];
                else if (j==0) maxLeft = nums1[i-1];
                else maxLeft = max(nums1[i-1], nums2[j-1]);
              	// 及时返回,防止nums1为空数组的时候访问nums1
                if ((m+n)%2==1) return maxLeft;

                if (i==m) minRight = nums2[j];
                else if (j==n) minRight = nums1[i];
                else minRight = min(nums1[i], nums2[j]);
                return (maxLeft+minRight)/2.0;
            }
        }
        return 0.0;
    }
};