239. 滑动窗口最大值
思路:这个维护元素单调递减的队列就叫做单调队列,即单调递减或单调递增的队列。C++中没有直接支持单调队列,需要我们自己来实现一个单调队列
代码实现
var maxSlidingWindow = function (nums, k) {
class MonoQueue {
constructor() {
this.queue = []
}
enqueue (value) {
let back = this.queue[this.queue.length - 1]
while (back !== undefined && back < value) {
this.queue.pop()
back = this.queue[this.queue.length - 1]
}
this.queue.push(value)
}
dequeue (value) {
let front = this.front()
//如果下一个元素跟 出口元素一样大 则删除出口元素
if (front === value) {
this.queue.shift()
}
}
front () {
return this.queue[0]
}
}
let helperQueue = new MonoQueue()
let i = 0, j = 0
let resArr = []
while (j < k) {
helperQueue.enqueue(nums[j++])
}
//resArr记录前K个元素的最大值
resArr.push(helperQueue.front())
while (j < nums.length) {
helperQueue.enqueue(nums[j])
helperQueue.dequeue(nums[i])
resArr.push(helperQueue.front())
i++, j++
}
return resArr
}
347.前 K 个高频元素
思路:
- 要统计元素出现频率
- 对频率排序
- 找出前K个高频元素
代码实现
class Heap {
constructor(compareFn) {
this.compareFn = compareFn
this.queue = []
}
push (item) {
// console.log(item)
this.queue.push(item)
let index = this.size() - 1 //记录推入元素下标志
let parent = Math.floor((index - 1) / 2)//记录父节点下标
while (parent >= 0 && this.compare(parent, index) > 0) {
[this.queue[index], this.queue[parent]] = [this.queue[parent], this.queue[index]];
index = parent
parent = Math.floor((index - 1) / 2)
}
}
pop () {
const out = this.queue[0]
this.queue[0] = this.queue.pop()
let index = 0;
let left = 1// left 是左子节点下标 left + 1 则是右子节点下标
let searchChild = this.compare(left, left + 1) > 0 ? left + 1 : left
while (searchChild !== undefined && this.compare(index, searchChild) > 0) {
[this.queue[index], this.queue[searchChild]] = [this.queue[searchChild], this.queue[index]];
index = searchChild;
left = 2 * index + 1;
searchChild = this.compare(left, left + 1) > 0 ? left + 1 : left;
}
return out
}
size () {
return this.queue.length
}
//使用传入的comparefn比较两个位置的元素
compare (index1, index2) {
// 处理下标越界问题
if (this.queue[index1] === undefined) return 1;
if (this.queue[index2] === undefined) return -1;
return this.compareFn(this.queue[index1], this.queue[index2]);
}
}
const topKFrequent = function (nums, k) {
const map = new Map()
for (const num of nums) {
map.set(num, (map.get(num) || 0) + 1)
}
console.log(map)
// 创建小顶堆
const heap = new Heap((a, b) => a[1] - b[1]);
for (const entry of map.entries()) {
heap.push(entry)
if (heap.size() > k) {
heap.pop()
}
}
const res = []
for (let i = heap.size() - 1; i >= 0; i--) {
res[i] = heap.pop()[0]
}
return res
}
总结
在栈与队列系列中,我们强调栈与队列的基础,也是很多同学容易忽视的点。
使用抽象程度越高的语言,越容易忽视其底层实现,而C++相对来说是比较接近底层的语言。
我们用栈实现队列,用队列实现栈来掌握的栈与队列的基本操作。
接着,通过括号匹配问题、字符串去重问题、逆波兰表达式问题来系统讲解了栈在系统中的应用,以及使用技巧。
通过求滑动窗口最大值,以及前K个高频元素介绍了两种队列:单调队列和优先级队列,这是特殊场景解决问题的利器,是一定要掌握的。