记录 1 道算法题
寻找两个正序数组的中位数
4. 寻找两个正序数组的中位数 - 力扣(LeetCode)
要求:提供两个升序的数组,然后求出两个数组合并起来后的中位数。时间复杂度O(m+n)。
比如:nums1: [1,3] nums2: [2]
输出: 2。
如果不考虑时间复杂度的条件,只需要将两个数组进行归并就可以得到中位数,甚至只需要两个指针就可以得出。
考虑时间复杂度就只能使用二分,减少一部分遍历。中位数也就是第 n 个数,所以我们只要判断当前的数是不是第 n 个数。
首先将中位数是第 n 个数,将 n 平均分给两个数组,即每个数组得到的是 (n / 2) - 1
位置上的数,就是第 n / 2
个。然后将两个数组的这个位置的数相比较,如果小的一方,那么可以判断为是前 n 个数中的一部分。
n 是 3.5 就是前 (7 / 2) + 1 = 4 个数
平均分配后 4 / 2 = 2
nums1: 1 2 3 4
↑
nums2: 5 6 7
↑
2 < 6
所以 1 和 2 属于 n 个数里面,移动指针,重新分配剩余的 n - 2。
nums1: 1 2 3 4
nums2: 5 6 7
还需要一些边界情况的考虑,比如由于长短不同,有一个数组不够分配。或者指针移到了数组的最后一个位置。
完整代码如下:
function findMedianSortedArrays(nums1, nums2) {
const total = nums1.length + nums2.length
// 算出中位数是第几个,这里并不是下标
const mid = total >> 1
if (total % 2 === 1) {
// 奇数个
// 小数的情况下向下取整,所以要加 1
return findKth(nums1, nums2, mid + 1)
} else {
// 偶数个
// 只需要求两个不同的位置即可
return (findKth(nums1, nums2, mid) + findKth(nums1, nums2, mid)) / 2
}
}
function findKth(nums1, nums2, k) {
// 两个指针
let s1 = 0
let s2 = 0
while(true) {
// 考虑一开始就已经被分好的情况
// 当第一个数组被分配完,意味着中位数是第二个数组,第二个数组分配剩下的 k 个位置。
if (s1 === nums1.length) {
return nums2[s2 + k - 1]
}
// 第二个数组先分配完,同理
if (s2 === nums2.length) {
return nums1[s1 + k - 1]
}
// 如果 k 刚好分配剩下一个,那么小的数就是排在前面的第 n 个
if (k === 1) {
return Math.min(nums1[s1], nums2[s2])
}
// 分配 k 给两个数组
let half = (k / 2) >> 1
// 第一个数组被分配到的位置
let newS1 = s1 + half - 1
// 第二个数组被分配到的位置
let newS2 = s2 + half - 1
// 当数组没有超出边界的时候
const a = nums1[newS1]
const b = nums2[newS2]
if (newS1 <= nums1.length - 1 && newS2 <= nums2.length - 1) {
if (a < b) {
// 如果第一个数组的数小,则归类进前 n 个。
// 计算剩余的 k
k -= (newS1 - s1 + 1)
// 移动指针
s1 = newS1 + 1
} else {
// 如果第二个数组的数小
k -= (newS2 - s2 + 1)
s1 = newS2 + 1
}
} else if (newS1 <= nums1.length - 1) {
// 只有第一个数组还有数的时候
// 只减 n1
k -= newS1 - s1 + 1
s1 = newS1 + 1
} else if (newS2 <= nums2.length - 1) {
// 只有第二个数组还有数的时候
// 只减 n2
k -= newS2 - s2 + 1
s2 = newS2 + 1
}
}
}