给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
将两个数组合并排序,然后返回中位数
题目是求中位数,其实就是求第 k 小数的一种特殊情况,而求第 k 小数有一种算法。
由于数列是有序的,其实我们完全可以一半儿一半儿的排除。假设我们要找第 k 小数,我们可以每次循环排除掉 k/2 个数。
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number}
*/
var findMedianSortedArrays = function (nums1, nums2) {
let n = nums1.length
let m = nums2.length
let left = Math.floor((n + m + 1) / 2)
let right = Math.floor((n + m + 2) / 2)
//将偶数和奇数的情况合并,如果是奇数,会求两次同样的k
return (getKth(nums1, 0, n - 1, nums2, 0, m - 1, left) + getKth(nums1, 0, n - 1, nums2, 0, m - 1, right)) * 0.5
};
function getKth(nums1, start1, end1, nums2, start2, end2, k) {
let len1 = end1 - start1 + 1
let len2 = end2 - start2 + 1
if (len1 > len2) return getKth(nums2, start2, end2, nums1, start1, end1, k)
// 保证如果有数组空了,一定是 len1
if (len1 == 0) return nums2[start2 + k - 1]
// 如果len1空了,直接返回len2第k位
if (k == 1) return Math.min(nums1[start1], nums2[start2])
let i = start1 + Math.min(len1, Math.floor(k / 2)) - 1
let j = start2 + Math.min(len2, Math.floor(k / 2)) - 1
if (nums1[i] > nums2[j]) {
return getKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1))
}
else {
return getKth(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1))
}
}
尾递归: 若函数在尾位置调用自身(或是一个尾调用本身的其他函数等等),则称这种情况为尾递归。尾递归也是递归的一种特殊情形。尾递归是一种特殊的尾调用,即在尾部直接调用自身的递归函数。对尾递归的优化也是关注尾调用的主要原因。
尾递归在普通尾调用的基础上,多出了2个特征
- 在尾部调用的是函数自身 (Self-called)
- 可通过优化,使得计算仅占用常量栈空间 (Stack Space)
上述的方法最多需要执行 2n 个步骤。事实上,它可以被进一步优化为仅需要 n 个步骤。我们可以定义字符到索引的映射,而不是使用集合来判断一个字符是否存在。 当我们找到重复的字符时,我们可以立即跳过该窗口。
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number}
*/
var findMedianSortedArrays = function (nums1, nums2) {
let m = nums1.length
let n = nums2.length
if (m > n) {
let temp = nums1; nums1 = nums2; nums2 = temp
let tmp = m; m = n; n = tmp
}
let iMin = 0, iMax = m, halfLen = Math.floor((m + n + 1) / 2)
while (iMin <= iMax) {
let i = Math.floor((iMin + iMax) / 2)
let j = halfLen - i
if (i < iMax && nums2[j - 1] > nums1[i]) {
iMin = i + 1
}
else if (i > iMin && nums1[i - 1] > nums2[j]) {
iMax = i - 1
}
else {
let maxLeft = 0;
if (i == 0) { maxLeft = nums2[j - 1] }
else if (j == 0) { maxLeft = nums1[i - 1] }
else { maxLeft = Math.max(nums1[i - 1], nums2[j - 1]) }
if ((m + n) % 2 == 1) { return maxLeft }
let minRight = 0
if (i == m) { minRight = nums2[j] }
else if (j == n) { minRight = nums1[i] }
else { minRight = Math.min(nums2[j], nums1[i]) }
return (maxLeft + minRight) / 2.0
}
}
return 0.0
}