每日一题(2020-05-24)4. 寻找两个正序数组的中位数

[4. 寻找两个正序数组的中位数]

难度 困难

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

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

你可以假设 nums1nums2 不会同时为空。

示例 1:

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

则中位数是 2.0

示例 2:

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

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays

解法:二分查找

我们可以将找中位数的问题转化为求第 k 小数,根据中位数的定义,当 m+n 是奇数时,中位数是两个有序数组中的第 (m+n)/2 个元素,当 m+n 是偶数时,中位数是两个有序数组中的第 (m+n)/2 个元素和第 (m+n)/2+1 个元素的平均值。因此,这道题中 k 为 (m+n)/2 或 (m+n)/2+1。

设两个有序数组分别为A和B。要找到第k个元素,我们可以比较 A[k/2 - 1] 和 B[k/2 - 1]

  1. 如果A[k/2 - 1] < B[k/2 - 1],则比 A[k/2 - 1] 小的数最多只有 A 中的前 k/2 - 1 个数和 B 中的前 k/2 - 1 ,因此最多总共有 k - 2 个数比 A[k/2 - 1] 小, 因此从 A[0] 到 A[k/2 - 2] 都不是中位数,可以全部排出。
  2. 同理如果 B[k/2 - 1] < A[k/2 - 1] , 则排出 B[0] 到 B[k/2 - 2]
  3. 如果B[k/2 - 1] = A[k/2 - 1], 我们既可以排出 A[0] 到 A[k/2 - 2] 也可以 排出 B[0] 到 B[k/2 - 2] ,因此将其归纳到情况一中统一处理

除此之外两个数组中剩余元素个数可能不足 k/2 个,因此为了防止数组长度小于 k/2,所以每次比较 min(k/2,len(数组)) 对应的数字,把小的那个对应的数组的数字排除,将两个新数组进入递归,并且 k 要减去排除的数字的个数。

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int length1 = nums1.length, length2 = nums2.length;
        int totalLength = length1 + length2;
        if (totalLength % 2 == 1) {
            int midIndex = totalLength / 2;
            double median = getKthElement(nums1, nums2, midIndex + 1);
            return median;
        } else {
            int midIndex1 = totalLength / 2 - 1, midIndex2 = totalLength / 2;
            double median = (getKthElement(nums1, nums2, midIndex1 + 1) + getKthElement(nums1, nums2, midIndex2 + 1)) / 2.0;
            return median;
        }
    }

    public int getKthElement(int[] nums1, int[] nums2, int k) {
        int length1 = nums1.length, length2 = nums2.length;
        int index1 = 0, index2 = 0;
        int kthElement = 0;

        while (true) {
            // 边界情况
            if (index1 == length1) {
                return nums2[index2 + k - 1];
            }
            if (index2 == length2) {
                return nums1[index1 + k - 1];
            }
            if (k == 1) {
                return Math.min(nums1[index1], nums2[index2]);
            }
            
            // 正常情况
            int half = k / 2;
            int newIndex1 = Math.min(index1 + half, length1) - 1;
            int newIndex2 = Math.min(index2 + half, length2) - 1;
            int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2];
            if (pivot1 <= pivot2) {
                k -= (newIndex1 - index1 + 1);
                index1 = newIndex1 + 1;
            } else {
                k -= (newIndex2 - index2 + 1);
                index2 = newIndex2 + 1;
            }
        }
    }
}

你可能感兴趣的:(LeetCode,每日一题)