Leetcode刷题:寻找两个正序数组的中位数

399 阅读3分钟

这是我参与新手入门的第1篇文章。

前言

LetCode第4题 寻找两个正序数组的中位数 题目描述如下:

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

示例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

示例3:

输入:nums1 = [0,0], nums2 = [0,0]
输出:0.00000

一、解题思路

这个题目可能看起来不太好理解,中位数是指按顺序排列的一组数据中居于中间位置的数,此题中寻找两个正序数组的中位数可以理解为合并两个数组后找到其中位数。此处给出如下解法:

  • 使用归并的方式,合并两个有序数组,得到一个大的有序数组。大的有序数组的中间位置的元素,即为中位数。

  • 不需要合并两个有序数组,只要找到中位数的位置即可。由于两个数组的长度已知,因此中位数对应的两个数组的下标之和也是已知的。维护两个指针,初始时分别指向两个数组的下标 00 的位置,每次将指向较小值的指针后移一位(如果一个指针已经到达数组末尾,则只需要移动另一个数组的指针),直到到达中位数的位置。

二、代码实现

方法一:暴力合并数组

思路:

直接利用JS的concat()方法直接合并两个数组,然后使用sort()方法对其进行排序

代码:

var findMedianSortedArrays = function (nums1, nums2) {
    let temp = nums1.concat(nums2).sort((a, b) => {
        return a - b;
    });
    if (temp.length % 2 !== 0) {
        return temp[(temp.length - 1) / 2];
    } else {
        return (temp[temp.length / 2] + temp[(temp.length / 2) - 1]) / 2;
    }
};

结果:

2021-07-06_212251.png

方法二:二分查找

思路:

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

代码:

var findMedianSortedArrays = function(nums1, nums2) {
    // nums1长度比nums2小
    if (nums1.length > nums2.length) {
        [nums1, nums2] = [nums2, nums1];
    }

    let m = nums1.length;
    let n = nums2.length;
    // 在0~m中查找
    let left = 0;
    let right = m;

    // median1:前一部分的最大值
    // median2:后一部分的最小值
    let median1 = 0;
    let median2 = 0;

    while(left <= right) {
        // 前一部分包含 nums1[0 .. i-1] 和 nums2[0 .. j-1]
        // 后一部分包含 nums1[i .. m-1] 和 nums2[j .. n-1]
        const i = left + Math.floor((right - left) / 2);
        const j = Math.floor((m + n + 1) / 2) - i;

        const maxLeft1 = i === 0 ? -Infinity : nums1[i - 1];
        const minRight1 = i === m ? Infinity : nums1[i];

        const maxLeft2 = j === 0 ? -Infinity : nums2[j - 1];
        const minRight2 = j === n ? Infinity : nums2[j];

        if (maxLeft1 <= minRight2) {
            median1 = Math.max(maxLeft1, maxLeft2);
            median2 = Math.min(minRight1, minRight2);
            left = i + 1;
        } else{
            right = i - 1;
        }
    }
    return (m + n) % 2 == 0 ? (median1 + median2) / 2 : median1;
};

时间复杂度O(log(min(m, n))),n为nums1的长度,m为nums2的长度

结果:

2021-07-06_213234.png

三、总结

这道题本身并不难,使用暴力方法很容易就可实现,但难点在于题目限制了时间复杂度 O(log (m+n)) ,由此很容易想到使用二分查找来解题,此题实际上实在考察对于算法及其时间复杂度的考察。