目录:算法日记
原题链接:4. 寻找两个正序数组的中位数 - 力扣(LeetCode) (leetcode-cn.com)
题目描述
给定两个大小分别为m和n的正序(从小到大)数组nums1和nums2。请你找出并返回这两个正序数组的中位数。
算法的时间复杂度应该为。
数据范围
题目示例
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
算法思路
给定两个正序数组和,设对应数组长度为和,令。求两个正序数组中位数,相当于求两个正序数组中第(k从1开始) 小的数,设表示两个正序数组中第小数值:
- 当为奇数时,,中位数为;
- 当为偶数时,,中位数为; 为方便讨论,令,区间,区间。
考虑一般情况,当且时,有如下三种情况:
- ,此时中位数必然不在区间中,故该区间可舍去;
- ,此时中位数必然不在区间中,故该区间可舍去;
- ,此时二者都为中位数,可舍弃一个,因此该情况可与上述两种情况中的任意一种合并; 每次舍去后,的值对应减少舍去的长度,转变为一个递归子问题。 相当于每次对和中的任意一个砍半,时间复杂度满足。
考虑特殊情况:
- 由于且,因此:
- ;
- 当时,为统一操作,将中所有元素全部取出;
- 由于较短,合并一般情况中的1和3;
- 当时,按第小数定义,取和的最小值;
- 当先被划分完时,直接返回的第个元素;
AC代码
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number}
*/
var findMedianSortedArrays = function(nums1, nums2) {
let len = nums1.length + nums2.length;
if(len % 2 == 1) {
return find(nums1, 0, nums2, 0, (len >> 1) + 1);
} else {
// f(k1)
let left = find(nums1, 0, nums2, 0, (len >> 1));
// f(k2)
let right = find(nums1, 0, nums2, 0, (len >> 1) + 1);
return (left + right) / 2;
}
};
var find = function(nums1, l1, nums2, l2, k) {
// 保证nums2比nums1长
if(nums1.length - l1 > nums2.length - l2) return find(nums2, l2, nums1, l1, k);
// 如果nums1已被全部舍去
if(nums1.length === l1) return nums2[l2 + k - 1];
if(k === 1) return Math.min(nums1[l1], nums2[l2]);
// len1可能小于k
let tl1 = Math.min(l1 + (k >> 1), nums1.length);
let tl2 = l2 + (k >> 1);
// 一般情况
if(nums1[tl1 - 1] > nums2[tl2 - 1]) {
return find(nums1, l1, nums2, l2 + (k >> 1), k - (k >> 1));
} else {
return find(nums1, tl1, nums2, l2, k - (tl1 - l1));
}
}