题目

给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。

请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

示例 1:

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

则中位数是 2.0
示例 2:

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

则中位数是 (2 + 3)/2 = 2.5

思路

Reference:https://blog.csdn.net/chen_xinjia/article/details/69258706
这道题要求对数级别的空间复杂度,因此肯定要使用binary-search。

中位数是什么?中位数指的是,在一个数组里,有一个数,在此数左边的值均小于等于此数,在此数右边的值均大于等于此数,且左边数的数量和右边数的数量相同。

在此道题目题目里,给定的数组已经满足了有序性,所以我们需要做的是,将nums1和nums2在逻辑上看为同一个数组,我们在脑海里将其命名为nums3,找到一个数,保证在nums3里,此数的左右两侧数目相同。

我们于是使用一个“切”的说法,将nums1和nums2切开左右两边,保证:

  1. nums1切割处的左边小于nums1切割处的右边(由于是有序数组,所以这一点无需我们担忧)
  2. nums2切割处的左边小于nums2切割处的右边(由于是有序数组,所以这一点无需我们担忧)
  3. nums1切割出的左边小于nums2切割处的右边(此处需要我们进行判断)
  4. nums2切割处的左边小于nums1切割处的右边(此处需要我们进行判断)

由于nums3的数量固定,所以逻辑上的切分处,两边数目固定,为nums3.size()/2=(nums1.size()+nums2.size())/2。将两个数组切割处为cut1,cut2,不难得出cut2=nums3.size()/2-cut1。

于是我们只需要知道nums1的切割处,即可知道nums2的切割处。

问题在此转换为确认nums1切割处,而nums1切割处只可能是nums1数组开头处、nums1中间和nums2末尾,此时便可使用binary_search,对nums1切割处的合法性进行判断。

什么是nums1切割处的合法性?之前已经提过了哦。

  1. nums1切割处的左边小于nums1切割处的右边(由于是有序数组,所以这一点无需我们担忧)
  2. nums2切割处的左边小于nums2切割处的右边(由于是有序数组,所以这一点无需我们担忧)
  3. nums1切割出的左边小于nums2切割处的右边(此处需要我们进行判断)
  4. nums2切割处的左边小于nums1切割处的右边(此处需要我们进行判断)

如果这些条件都合法,那么我们就返回结果就行了。

解法

class Solution {
 public:
  double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
    int size1 = nums1.size(), size2 = nums2.size(), size = size1 + size2;

    //保证nums1大小较短,这也是缩短时间的一个好方法。
    if (size1 > size2) return findMedianSortedArrays(nums2, nums1);

    //如果nums1为空,那么答案就是nums2中间值。此处写的好像很复杂,实际上这条式子综合了nums2数目为奇数和偶数这两种状况。
    if (size1 == 0)
      return ((double)nums2[(size2 - 1) / 2] + (double)nums2[(size2 / 2)]) / 2;

    //cut1的取值范围为cutL和cutR。初始化时,cutL和cutR分别在nums1的头尾处
    int cutL = 0, cutR = size1;
    //cut1初始化为中值。根据前面分析,只需要知道cut1,便可知道cut2处
    int cut1 = size1 / 2;
    int cut2 = size - cut1;

    while (cut1 <= size1) {
      //此处取得nums1切割处和nums2切割处的两边的值
      cut1 = (cutR - cutL) / 2 + cutL;
      cut2 = size / 2 - cut1;

      double L1 = (cut1 == 0) ? INT_MIN : nums1[cut1 - 1];
      double L2 = (cut2 == 0) ? INT_MIN : nums2[cut2 - 1];
      double R1 = (cut1 == size1) ? INT_MAX : nums1[cut1];
      double R2 = (cut2 == size2) ? INT_MAX : nums2[cut2];

      //此处为合法性判断
      if (L1 > R2)
        cutR = cut1 - 1;
      else if (L2 > R1)
        cutL = cut1 + 1;
      else {
        //如果合法,则根据奇偶情况返回值
        if (size % 2 == 0) {
          L1 = ((L1 > L2) ? L1 : L2);
          R1 = ((R1 > R2) ? R2 : R1);
          return (L1 + R1) / 2;
        } else {
          R1 = (R1 < R2 ? R1 : R2);
          return R1;
        }
      }
    }
    return 0;
  }
};