LeetCode 第四题:寻找两个正序数组的中位数

14 阅读3分钟

题目描述

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

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

示例 1:

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

示例 2:

输入: nums1 = [1,2], nums2 = [3,4]
输出: 2.50000
解释: 合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

提示:

  • nums1.length == m
  • nums2.length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000
  • -106 <= nums1[i], nums2[i] <= 106

解题思路

**划分数组:**首先需要理解中位数是什么。

 将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。
  1. 那么一个数组找中位数,我们就可以对中位数分割,例如 [1 2 3 4],中位数就是 2 3,目标就是寻找 2 3。
  2. 左半部分的数都大于右半部分的数。
  3. 左部分的数要等于右半部分的个数,但是如果长度为奇数,就让左半部分比右半部分多一个数,分割左边的数就是想要的中位数。
  4. 扩展到两个数组的时,如[1 2 3 4] [5 6 7 8 9 10 11 12]。
  5. 1 2 3 4 ||
    5 6 || 7 8 9 10 11 12
  6. 知道第一个数组的分割线就可以求得第二个数组的分割线。
  7. 在偶数的情况下:第一个数组的长度为 n 分割线前面有 i 个元素,第二个数组的长度为 m 分割线前面有 j 个元素,两个数组分割线左部分的元素的个数和等于右部分的分割线的和,j = (m + n) / 2 - i。
  8. 在奇数的情况下:左部分多一个元素, i + j = n - i + m - j + 1,左部分多要在右部分 + 1 才能使左右部分元素个数相等,即 j = (m + n + 1) / 2 - i。
  9. 如 [1 2 3 4] [5 6 7 8 9 10 11 12 13]
  10. 1 2 3 4 ||
    5 6 7 || 8 9 10 11 12 13
  11. 但是 i 和 j 为整型,+1 在除以 2 不影响结果,所以总长度奇偶数都可以使用 j = (m + n + 1) / 2 - i。(即 13 / 2 = 6)
/**
 * @ClassName FindMedianSortedArrays
 * @Description 寻找两个正序数组的中位数
 * @Version 1.0.0
 * @Date 2024/6/3 23:54
 * @Author By Dwl
 */
public class FindMedianSortedArrays {
    
    public static void main(String[] args) {
        int[] numsOne = new int[]{1, 2};
        int[] numsTwo = new int[]{3, 4};
        double d = findMedianSortedArrays(numsOne, numsTwo);

        System.out.println(d);
    }
    
    public static double findMedianSortedArrays(int[] numsOne, int[] numsTwo) {
        if (numsOne.length > numsTwo.length) {
            return findMedianSortedArrays(numsTwo, numsOne);
        }

        // 获取长度下标
        int m = numsOne.length;
        int n = numsTwo.length;

        // 定位指针
        int left = 0, right = m;
        // 获取中位数
        int medianOne = 0, medianTwo = 0;

        while (left <= right) {
            // 获取中位数的指针
            int i = (left + right) / 2;
            // 取中位数旁边的指针
            int j = (m + n + 1) / 2 - i;

            // 获取中位数的值
            int numsIm = (i == 0 ? Integer.MIN_VALUE : numsOne[i - 1]);
            int numsI = (i == m ? Integer.MAX_VALUE : numsOne[i]);
            int numsJm = (j == 0 ? Integer.MIN_VALUE : numsTwo[j - 1]);
            int numsJ = (j == n ? Integer.MAX_VALUE : numsTwo[j]);

            if (numsIm <= numsJ) {
                medianOne = Math.max(numsIm, numsJm);
                medianTwo = Math.min(numsI, numsJ);

                left = i + 1;
            } else {
                right = i - 1;
            }
        }

        return (m + n) % 2 == 0 ? (medianOne + medianTwo) / 2.0 : medianOne;
    }
}