PriorityQueue - 优先队列:正常⼊、按照优先级出
实现机制
- Heap (Binary, Binomial, Fibonacci)
- Binary Search Tree
最小堆实现
class MinHeap {
constructor(data = []) {
this.data = data
// heapify
if(this.size() > 1) {
for (let i = 0; i < this.size(); i++) {
this.bubbleUp(this.data[i])
}
}
}
offer(v) {
this.data.push(v)
this.bubbleUp(this.size() - 1)
}
peek() {
if (this.size() === 0) return null
return this.data[0]
}
poll() {
if(this.size() === 0) return null
const res = this.data[0]
const last = this.data.pop()
if(this.size() > 0) {
this.data[0] = last
this.bubbleDown(0)
}
return res
}
bubbleUp(index) {
while (index > 0) {
const parentIndex = (index - 1) >>> 1
if (this.minus(index, parentIndex) < 0) {
this.swap(index, parentIndex);
index = parentIndex
} else {
break
}
}
}
bubbleDown(index) {
const lastIndex = this.size() - 1
while (1) {
const l = index * 2 + 1
const r = index * 2 + 2
let findIndex = index
if(l <= lastIndex && this.minus(l, findIndex) < 0) {
findIndex = l
}
if(r <= lastIndex && this.minus(r, findIndex) < 0) {
findIndex = r
}
if (index !== findIndex) {
this.swap(index, findIndex)
index = findIndex
} else {
break
}
}
}
swap(index1, index2) {
[this.data[index1], this.data[index2]] = [this.data[index2], this.data[index1]];
}
size() {
return this.data.length
}
minus(a,b) {
return this.data[a] - this.data[b]
}
}
703. 数据流中的第 K 大元素
维护一个大小为 k 的最小堆
添加时,堆大小超过 K , 就去掉堆顶元素
最后,保留的都是所有数据流中最大的元素,而堆顶正好是第 K 大的元素
var KthLargest = function(k, nums) {
this.k = k
this.heap = new MinHeap()
for (const x of nums) {
this.add(x)
}
};
KthLargest.prototype.add = function(val) {
this.heap.offer(val)
if (this.heap.size() > this.k) {
this.heap.poll()
}
return this.heap.peek()
};
class MinHeap {
constructor(data = []) {
this.data = data
// heapify
if(this.size() > 1) {
for (let i = 0; i < this.size(); i++) {
this.bubbleUp(this.data[i])
}
}
}
offer(v) {
this.data.push(v)
this.bubbleUp(this.size() - 1)
}
peek() {
if (this.size() === 0) return null
return this.data[0]
}
poll() {
if(this.size() === 0) return null
const res = this.data[0]
const last = this.data.pop()
if(this.size() > 0) {
this.data[0] = last
this.bubbleDown(0)
}
return res
}
bubbleUp(index) {
while (index > 0) {
const parentIndex = (index - 1) >>> 1
if (this.minus(index, parentIndex) < 0) {
this.swap(index, parentIndex);
index = parentIndex
} else {
break
}
}
}
bubbleDown(index) {
const lastIndex = this.size() - 1
while (1) {
const l = index * 2 + 1
const r = index * 2 + 2
let findIndex = index
if(l <= lastIndex && this.minus(l, findIndex) < 0) {
findIndex = l
}
if(r <= lastIndex && this.minus(r, findIndex) < 0) {
findIndex = r
}
if (index !== findIndex) {
this.swap(index, findIndex)
index = findIndex
} else {
break
}
}
}
swap(index1, index2) {
[this.data[index1], this.data[index2]] = [this.data[index2], this.data[index1]];
}
size() {
return this.data.length
}
minus(a,b) {
return this.data[a] - this.data[b]
}
}
239. 滑动窗口最大值
最大堆实现
var maxSlidingWindow = function(nums, k) {
let n = nums.length;
let pq = new MaxHeap()
for (let i = 0; i < k; ++i) {
pq.offer([nums[i], i]);
}
let res = [pq.peek()[0]]
for (let i = k; i < n; i++) {
pq.offer([nums[i], i])
while (pq.peek()[1] <= i - k) {
pq.poll()
}
res.push(pq.peek()[0])
}
return res
};
class MaxHeap {
constructor(data = []) {
this.data = data
// heapify
if(this.size() > 1) {
for (let i = 0; i < this.size(); i++) {
this.bubbleUp(this.data[i])
}
}
}
offer(v) {
this.data.push(v)
this.bubbleUp(this.size() - 1)
}
peek() {
if (this.size() === 0) return null
return this.data[0]
}
poll() {
if(this.size() === 0) return null
const res = this.data[0]
const last = this.data.pop()
if(this.size() > 0) {
this.data[0] = last
this.bubbleDown(0)
}
return res
}
bubbleUp(index) {
while (index > 0) {
const parentIndex = (index - 1) >>> 1
if (this.minus(index, parentIndex) > 0) {
this.swap(index, parentIndex);
index = parentIndex
} else {
break
}
}
}
bubbleDown(index) {
const lastIndex = this.size() - 1
while (1) {
const l = index * 2 + 1
const r = index * 2 + 2
let findIndex = index
if(l <= lastIndex && this.minus(l, findIndex) > 0) {
findIndex = l
}
if(r <= lastIndex && this.minus(r, findIndex) > 0) {
findIndex = r
}
if (index !== findIndex) {
this.swap(index, findIndex)
index = findIndex
} else {
break
}
}
}
swap(index1, index2) {
[this.data[index1], this.data[index2]] = [this.data[index2], this.data[index1]];
}
size() {
return this.data.length
}
minus(a,b) {
return this.data[a][0] - this.data[b][0]
}
}
双向队列
最右边永远是最大的
var maxSlidingWindow = function(nums, k) {
const n = nums.length
const index = []
for (let i = 0; i < k; i++) {
while (index.length && nums[i] >= nums[index[index.length - 1]]) {
// 因为后面比前面大,所以前面的永远不可能会出现,于是直接 pop() ,后面根据 index[0] <= i - k 判断 index 是否在窗口中
index.pop()
}
index.push(i)
}
let res = [nums[index[0]]]
for (let i = k; i < n; i++) {
while (index.length && nums[i] >= nums[index[index.length - 1]]) {
index.pop()
}
index.push(i)
// 判断 index[0] 是否在窗口中,如果不再直接 shift 出去
while (index[0] <= i - k) {
index.shift()
}
res.push(nums[index[0]])
}
return res
};