LeetCode - 寻找两个正序数组的中位数

82 阅读1分钟

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

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

遍历合并数组

  • 遍历两个数组,合并为一个有序数组
  • 合并后的有序数组,根据长度奇数偶数取中值
function findMedin(arr1, arr2) {
    let arr = [];
    let i = 0;
    let j = 0;
    while (i < arr1.length || j < arr2.length) {
        // arr1数组遍历完,连接上arr2剩下的数组
        if (i === arr1.length) {
            arr = arr.concat(arr2.slice(j)); // concat是返回一个新数组
            break;
        }
        if(j === arr2.length) {
            arr = arr.concat(arr1.slice(i));
            break;
        }
        if (arr1[i] > arr2[j]) {
            arr.push(arr2[j]);
            j++;
        } else {
            arr.push(arr1[i]);
            i++;
        }

    }
    console.log(arr);
    // 找出中位数
    if (arr.length%2 === 0) {
        // 偶数
        let mid = arr.length / 2;
        return (arr[mid] + arr[mid+1]) / 2;
    } else {
        // 奇数
        return arr[parseInt(arr.length / 2) + 1];
    }

}

console.log(findMedin([1,3,6], [2,6,8,9]));

但是先合并数组的方式时间复杂度不满足要求,题目要求时间复杂度为O(log(m+n))

  • 有序数组
  • 时间复杂度为O(log(m+n)) 想到了什么?二分法

例如: 数组a = [1,3,5,7,9] 数组b = [2,4,6,8,10,12,14,16]

查找两个数组的第K大的数, 数组a中的第 k/2个元素(先假设k为偶数) 数组b中的第 k/2个元素 如果a[k/2 - 1] < b[k/2 - 1],那么第k个元素不可能在a[0]~a[k/2 - 1]中,那么排除了k/2

k为奇数时 数组a中的第 parseInt(k/2)个元素 数组b中的第 k - parseInt(k/2)个元素 同上

这个up主视频讲解思路挺清楚,没看懂建议反复观看。

function findMedin(arr1, arr2) {
    let len = arr1.length + arr2.length;
    if (len % 2 === 0){
        // 合并长度为偶数
        let left = findK(arr1, 0, arr2, 0, parseInt(len/2));
        let right = findK(arr1, 0, arr2, 0, parseInt(len/2) + 1);
        console.log('left = ' + left + ' ;right = ' + right);
        return parseFloat((left + right)/2);
    } else {
        // 合并长度为奇数
        return findK(arr1, 0, arr2, 0, parseInt(len/2) + 1);
    }    

}

// 返回两个数组中第K大的元素
function findK(arr1, i, arr2, j, k){
    // 处理边界情况
    if (arr1.length - i > arr2.length - j) {
        // 保证arr1数组长度短
        return findK(arr2, j, arr1, i, k);
    }
    // 处理有一个空数组的情况
    if (arr1.length === i) {
        return arr2[j + k - 1];
    }
    // k === 1
    if (k === 1) {
        return Math.min(arr1[i], arr2[j]);
    }
    let idx1 = Math.min(arr1.length, i + parseInt(k/2));
    let idx2 = j + k - parseInt(k/2);
    if (arr1[idx1 - 1] < arr2[idx2 - 1]) {
        return findK(arr1, idx1, arr2, j, k - (idx1 - i));
    } else {
        return findK(arr1, i, arr2, idx2, k - (idx2 - j));
    }
}

// console.log(findMedin([1,3,6], [2,6,8,9]));
console.log(findMedin([1,3], [2,6,8,9]));