思路:因为时间复杂是是logN,只有使用二分法才能满足要求。
假设两个数组分别是a和b,长度是m和n,我们要找的是m+n中的第k个数。
对a和b折半(先不考虑奇偶问题),分成a:0~i-1 | i~m-1,b:0~j-1 | j~n-1
①i+j=m+n-i-j👉j=(m+n)/2-i👉m<n才能保证j>0(因为i最大可以是m)
②**a[i-1]<b[j]和b[j-1]<a[i]**至少有一个会满足条件(不可能两个都不满足)因为如果b[j]<a[i-1], 又已知a[i-1]<a[i]和b[j-1]<b[j],可以得出b[j-1]<a[i]
③当a[i-1]>b[j]时,说明a数组取得两个值取大了,b数组取得两个值取小了👉a数组中的取的数左移(b数组中的取的数右移)
当b[j-1]>a[i]时,说明b数组取得两个值取大了,a数组取得两个值取小了👉b数组中的取的数左移(a数组中的取的数右移)
④a数组的左移右移都是指选择左半边的中位数/选择右半边的中位数(比如说a[i-1]>b[j],a数组取得两个值取大了,a数组的右半边一定都在最后中位数的右边,在a数组的左半边继续寻找中位数)
⑤加上奇偶来考虑,如果
-
m+n是偶数,左半边的数=右半边的数:i+j=m+n-i-j,j=(m+n)/2-i,最后的结果是左半边的最大者和右半边的最小者平均
-
m+n是偶数,左半边的数比右半边的数多一个:i+j=m+n-i-j+1,j=(m+n+1)/2-i,最后的结果是左半边的最大者
-
但如果把j表示成向下取整,可以统一j的表达式j=Math.floor((m+n+1)/2-i)
function findTwoSortedArrayMidian(a, b) {
// 保证m<n
if (a.length > b.length) {
let temp
temp = a
a = b
b = temp
}
let iMin = 0,
iMax = a.length,
m = a.length,
n = b.length,
halfLen = Math.floor((a.length + b.length + 1) / 2)
while (iMin < iMax) {
let i = Math.floor((iMin + iMax) / 2)
let j = halfLen - i
// a数组取值小了,右移(取右半边)
if (i < iMax && b[j - 1] > a[i]) {
iMin = i + 1
// a取值大了,左移(取左半边)
} else if (i > iMin && a[i - 1] > b[j]) {
iMax = i - 1
// 分割的点符合要求
} else {
let maxLeft = 0
// a数组全都在中位数右边
if (i === 0) {
maxLeft = b[j - 1]
// a数组全部在中位数左边
} else if (j == 0) {
maxLeft = a[i - 1]
// 一般情况
} else {
maxLeft = a[i - 1] > b[j - 1] ? a[i - 1] : b[j - 1]
}
// 奇数取左边最大值就可以
if ((m + n) % 2 === 1) {
return maxLeft
}
// 偶数取左边最大和右边最小的平均数
let minRight = 0
if (i == m) {
minRight = b[j]
} else if (j == n) {
minRight = a[i]
} else {
minRight = a[i] < b[j] ? a[i] : b[j]
}
return (maxLeft + minRight) / 2
}
}
return 0
}
let a = [1, 2, 3, 4],
b = [7, 10]
let mid = findTwoSortedArrayMidian(a, b)
console.log(mid)