算法学习记录-LeetCode4.寻找两个正序数组的中位数

135 阅读2分钟

LeetCode4.寻找两个正序数组的中位数

原题链接

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

题解

递归:时间复杂度O(log(n+m)) 我们将寻找两个有序数组的中位数可以看成找到第(n+m)/2个数字。
假设m,n >= k/2,我们先从nums1和nums2中各选取前k/2个元素:

  • 如果nums1[k/2 - 1] > nums2[k/2 - 1],则说明nums2中前k/2个元素一定小于第k个数,所以可以选择nums2中前k/2中数,然后继续在k-[k/2]中寻找。
  • 如果nums1[k/2 - 1] <= nums2[k/2 - 1],则说明nums1中前k/2个元素一定小于第k个数,所以可以选择nums1中前k/2中数,然后继续在k-[k/2]中寻找。

考虑边界情况: 如果 m<k/2,则我们从nums1中取m个元素,从nums2中取 k/2个元素(由于k=(n+m)/2,因此 m,n 不可能同时小于 k/2):

  • 如果nums1[m - 1] > nums2[k/2-1]则nums2中前k/2中元素一定都小于等于第k个数字。在剩下的数字找第k-[k/2]个数。
  • 如果nums1[m - 1] <= nums2[k/2-1]则nums1中前k/2中元素一定都小于等于第k个数字。因此第k小的数字是nums2[k-m-1]。
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int tot = nums1.length + nums2.length;
        if (tot % 2 == 0) {
            int left = find(nums1, 0, nums2, 0, tot / 2);
            int right = find(nums1, 0, nums2, 0, tot / 2 + 1);
            return (left + right) / 2.0;
        } else {
            return find(nums1, 0, nums2, 0, tot / 2 + 1);
        }
    }

    public int find(int[] nums1, int i, int[] nums2, int j, int k) {
        if (nums1.length - i > nums2.length - j) return find(nums2, j, nums1, i, k);
        if (k == 1) {
            if (nums1.length == i) return nums2[j];
            else return Math.min(nums1[i],nums2[j]);
        }
        if (nums1.length == i) return nums2[j + k - 1];
        int si = Math.min(i + k / 2,nums1.length), sj = j + k - k / 2;
        if (nums1[si - 1] > nums2[sj - 1]) return find(nums1, i, nums2, sj, k - (sj - j));
        else return find(nums1, si, nums2, j, k - (si - i)); 
    }

}