二分:寻找两个正序数组的中位数

98 阅读2分钟

leetcode: 4. 寻找两个正序数组的中位数

题目描述

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log (m+n)) 。

示例

输入: nums1 = [1,3], nums2 = [2]
输出: 2.00000
解释: 合并数组 = [1,2,3] ,中位数 2

解题思路

中位数 统一奇偶数处理:

  • 奇数:(n / 2) + 1 or n / 2 + 1
  • 偶数: (n / 2 + n / 2 + 1) / 2, n / 2 == (n + 1) / 2

find找到两个正序中第k小的数:

  • 保证nums1大小 < nums2,避免额外判断和处理。
  • nums1大小为0,直接从nums2中拿
  • k为1时,取两个数组首元素中更小的那个
  • 二分处理,每次舍去 k/2 的元素:
    • nums1取k一半或者较小一半的元素大小,nums2取另一半
    • 如果nums1拿到的最后一个元素 大于 nums2拿到的,那么第k大的元素肯定不在nums2拿到的这部分 舍去
    • 反之,舍去nums1拿到的这部分(相等的情况舍去小的部分)

完整代码:

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n = nums1.length + nums2.length;
        int l = find(nums1, 0, nums2, 0, (n + 1) / 2);
        int r = find(nums1, 0, nums2, 0, n / 2 + 1);
        return (l + r) / 2.0;
    }

    int find (int[] nums1, int start1, int[] nums2, int start2, int target) {
        int len1 = nums1.length - start1, len2 = nums2.length - start2;
        if (len1 > len2) {
            return find(nums2, start2, nums1, start1, target);
        }

        if (0 == len1) return nums2[start2 + target - 1];

        if (1 == target) return Math.min(nums1[start1], nums2[start2]);

        int l = len1 < target / 2 ? nums1.length - 1 : start1 + target / 2 - 1;
        int r = start2 + (target + 1) / 2 - 1;
        if (nums1[l] > nums2[r]) {
            return find(nums1, start1, nums2, r + 1, target - (target + 1) / 2);
        }
        else {
            return find(nums1, l + 1, nums2, start2, target - target / 2);
        }
    }
}