题目一 239. 滑动窗口最大值
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
思路
- 构造单调递减队列,主要有3个操作:
- push:如果当前队列的最后的元素小于当前值,元素出队,直到没有小的元素,这样队列保持单调递减
- pop:带参数,表示要出队的val,如果val等于队头元素,才需要操作出队,其他不需要,因为比队头小的元素都已经出队列了
- front:仅返回队头元素,不删除
- 用构造好的队列模拟滑动窗口移动
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
class MyQueue {
queue = [];
// 出栈:只有元素等于最左边的元素才出栈,否则不需要操作
pop(value) {
const queue = this.queue;
const top = this.front();
if (queue.length && value === top) {
queue.shift();
}
}
// 入队:如果当前元素大于队列中的元素,则将队列中元素出队,直到队列变为单调递减
push(value) {
const queue = this.queue;
while (queue.length && value > queue[queue.length - 1]) {
queue.pop();
}
queue.push(value);
}
// 取第一个元素
front() {
const top = this.queue[0];
return top;
}
}
var maxSlidingWindow = function(nums, k) {
const queue = new MyQueue();
const result = [];
// 先构造k个大小的队列
for (let i = 0; i < k; i++) {
queue.push(nums[i]);
}
// 把第一个放进去
result.push(queue.front())
// 遍历后面的元素,不断的出对左边的元素,入队当前元素,模拟滑动窗口
for (let i = k; i < nums.length; i++) {
queue.pop(nums[i-k]);
queue.push(nums[i]);
result.push(queue.front());
}
return result;
};
题目二 347. 前 K 个高频元素
思路
- 先统计每个元素出现的频率。
- 按照频率的大小构造小顶堆,这样最后留的k个元素就是高频的k元素,其他小的都出队了,小顶堆的保存的是value,比较大小的话,需要从map中根据key去取。
- 最小堆第0位保留,从第一位开始放置元素,这样索引方便计算。
- 构造小顶堆的过程
-
取map的前k个元素先构造小顶堆,从第一个非叶子节点开始进行节点调整,每个点依次调整为小顶堆
-
k后面的数字,每次和堆顶元素进行比较
- 如果小于堆顶元素,不做处理,
- 大于堆顶元素,则替换堆顶元素,并且将它调整为符合小顶堆的特性
-
调整元素
- 记录当前节点和左右孩子节点的最小索引
- 如果左孩子节点有效,且小于父亲节点,则更新为左孩子节点的索引
- 如果右孩子有效,且小于最小节点,则更新最小索引为右孩子节点
- 如果当前索引和父亲节点不相等,则进行调整,如果调整完了,再对minIndex节点进行递归调整。
-
var topKFrequent = function(nums, k) {
const map = {};
const len = nums.length;
for (let i = 0; i < len; i++) {
if (map[nums[i]]) {
map[nums[i]]++;
} else {
map[nums[i]] = 1;
}
}
const heap = [0];
let count = 0;
// 收集map的前k个元素构造小顶堆,heap里存的是key
Object.keys(map).forEach((key) => {
if (count++ < k) {
heap.push(key);
if (count === k) {
buildHeap(heap, map, k);
}
} else if (map[key] > map[heap[1]]) {
heap[1] = key;
heapify(heap, map, 1);
}
})
return heap.slice(1);
};
function buildHeap(heap, map, k) {
const start = Math.floor(k / 2);
for (let i = start; i > 0; i--) {
heapify(heap, map, i);
}
}
function heapify(heap, map, i) {
const len = heap.length;
let minIndex = i;
const leftChild = 2 * i;
if (leftChild <= len && map[heap[leftChild]] < map[heap[i]]) {
minIndex = leftChild;;
}
const rightChild = leftChild + 1;
if (rightChild <= len && map[heap[rightChild]] < map[heap[minIndex]]) {
minIndex = rightChild;
}
// 如果堆顶元素大于孩子节点,则和孩子节点进行交换
if (minIndex !== i) {
[heap[i], heap[minIndex]] = [heap[minIndex], heap[i]];
if (minIndex < len) {
heapify(heap, map, minIndex);
}
}
}