记录 1 道算法题
连续中值
要求:
* 加入一个新的值时,重新计算中位数
* 数组长度是偶数时,返回中间两个的平均值,奇数时,返回中间的数。
首先比较简单的做法就是每次推入到数组都做一次冒泡,保证顺序的准确。
function MedianFinder() {
this.data = []
}
MedianFinder.prototype.addNum = function (num) {
const { data } = this
data.push(num)
// 得到最新的结尾下标
let i = data.length - 1
while (i > 0) {
// 从后面开始进行冒泡,升序排列
if (data[i] < data[i - 1]) {
const temp = data[i]
data[i] = data[i - 1]
data[i - 1] = temp
}
i--
}
}
MedianFinder.prototype.findMedian = function () {
const { data } = this
let i
if (data.length === 0) return
let len = data.length - 1
// 用下标计算,如果下标不是偶数
if (len % 2 !== 0) {
i = len >> 1
return (data[i] + data[i + 1]) / 2
} else {
// 下标为偶数
i = len / 2
return data[i]
}
}
暴力解做完了,接下来看看优化的写法是怎么样的。
优化的方向就是减少排序次数,当数组长度为奇数时,可以将这个中位数拆成第 n 大的数。当数组长度为偶数时,可以将这个中位数拆成第 n 大的数和第 k 小的数,就是把数组给折成两半,只需要知道他们的边界的数,其他位置的顺序可以忽略。
[1,2,3,4] 中位数是 2 和 3 的平均数,
即 第二小的数和第二大的数。
[1,2,3] 中位数是 2,
即 第二小的数或第二大的数。
为此我们需要一个堆,找最大第 n 个用最小堆,比较规则:升序排列。找最小第 n 个用最大堆,比较规则:降序排列。
为了让两个堆保持数量均等,采用数组长度是偶数的时候,往左边的堆(最大堆)放。数组长度是奇数时往右边的堆(最小堆)放。左边放一个,右边放一个。
添加的部分逻辑比较重,每次添加之前都要判断是否应该放在另一个堆更合适,如果是就先放进另一个堆,然后取出堆顶的值。用这个值再继续正常的左边放一个,右边放一个。
总是要保证左边的堆的堆顶要小于右边的堆的堆顶,不然后续添加会乱掉。
ps:堆的代码看这篇文章,这里直接使用
function MedianFinder() {
this.count = 0
// 第 n 大找最小堆,升序排列,第 n 大其实是在n个数中最小的那个
this.minHeap = new Heap((a, b) => a - b)
// 第 n 小找最大堆,降序排列
this.maxHeap = new Heap((a, b) => b - a)
}
MedianFinder.prototype.addNum = function (num) {
if (this.count % 2 === 0) {
// 应该添加到左边的堆
// 和右边的堆顶比较,如果比右边堆顶还大说明一定比左边的都大,属于右边的堆。
if (num > this.minHeap.data[0]) {
// 存进去排序
this.minHeap.push(num)
// 再取出来
const val = this.minHeap.pop()
// 代替 num 添加到左边的堆。
this.maxHeap.push(val)
} else {
// 如果比右边的堆顶小就可以之间加到左边的堆
this.maxHeap.push(num)
}
} else {
// 应该加入右边的堆
// 比左边的堆顶小的时候,比右边堆任何一个都小,属于左边
if (num < this.maxHeap.data[0]) {
// 存进去排序
this.maxHeap.push(num)
// 再取出来
const val = this.maxHeap.pop()
// 代替 num 添加到右边的堆
this.minHeap.push(val)
} else {
// 如果比左边的堆顶大,就直接添加到右边的堆
this.minHeap.push(num)
}
}
this.count++
}
MedianFinder.prototype.findMedian = function () {
// 根据奇偶数取平均数还是直接取
if (this.count % 2 === 0) {
return (this.minHeap.data[0] + this.maxHeap.data[0]) / 2
} else {
return this.maxHeap.data[0]
}
}
结束