● 239. 滑动窗口最大值
【思路】先构造一个单调队列,队列里单调递增或者单调递减。因为是求最大值,所以队列里没有必要维护所有元素,只需要确保队列开头第一个元素是最大值即可。新加进来的元素如果比末尾元素小就 pop 末尾元素直到最大值为第一个元素。如果等于开头第一个元素就删除开头第一个元素。
【疑问】为什么在 class 类里写,自己用函数写就会死循环。至今还疑问!
var maxSlidingWindow = function(nums, k) {
class MonoQueue {
queue;
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.push(helperQueue.front());
while (j < nums.length) {
helperQueue.enqueue(nums[j]);
helperQueue.dequeue(nums[i]);
resArr.push(helperQueue.front());
i++, j++;
}
return resArr;
};
【思路】这道题可以用小顶堆的方法来解决,因为每次够了 k 个元素要 pop 时,用大顶堆会 pop 掉最大的元素,所以要用小顶堆,每次pop 掉最小的元素。但 js 没有堆,需要自己构造(自己对自己要求低点,别人构造好我直接拿来用了)。
// js 没有堆 需要自己构造
class Heap {
constructor(compareFn) {
this.compareFn = compareFn;
this.queue = [];
}
// 添加
push(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) { // 注意compare参数顺序
[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) { // 注意compare参数顺序
[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]);
}
}
var topKFrequent = function(nums, k) {
let map = new Map();
for (let k of nums) {
map.set(k, (map.get(k) || 0) + 1);
}
// 创建小顶堆
let 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;
};
-
总结栈和队列
js 没有栈和队列,可以用数组的方法自己实现。无非就是数组进出顺序不一样。- js 实现一个栈
class Stack{ constructor{ this.stack = [] } // 入栈 push(item) { this.stack.push(item) } // 出栈 pop() { // 如果栈为空,直接返回 null if (this.stack,length === 0) { return null; } this.stack.pop() } // 获取栈长度 getCount() { return this.stack.length } // 查看栈顶元素 peek() { return this.stack[this.getCount() - 1] } // 判断是否为空 isEmpty() { return this.getCount === 0 } }
- 自己实现一个队列
class Queue{ constructor() { this.queue = [] } // 入队 enQueue(item) { return this.queue.push(item) } // 出队 deQueue() { if (this.stack.length === 0) { return null; } return this.queue.shift() } // 获取队头 getHeader() { return this.queue[0] } // 获取队列长度 getlength() { return this.queue.length() } // 判断是否为空 isEmpty() { return this.getlength() === 0 } }