239. 滑动窗口最大值
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var maxSlidingWindow = function (nums, k) {
class MonoQueue { //定义一个有新结构的queue(新方法)
queue;
constructor() {
this.queue = [];
}
enqueue(value) { //==push
let back = this.queue[this.queue.length - 1]; //back=queue的最后一个数
while (back !== undefined && back < value) {//3 2<5——把2,3弹出
this.queue.pop();
back = this.queue[this.queue.length - 1];
}
this.queue.push(value); //5放前面
}
dequeue(value) { //shift()
let front = this.front(); //因为之前弹出了,就不管
if (front === value) {//如果要弹出的就是这个最前面的一个数,弹出
this.queue.shift();
}
}
front() { //返回这个数,(结构是被人用的,js的独有特性,不要被禁锢了)
return this.queue[0];
}
}
let helperQueue = new MonoQueue();
let i = 0, j = 0;
let resArr = [];//返回数组
while (j < k) {
helperQueue.enqueue(nums[j++]); //先放前k个进去
}
resArr.push(helperQueue.front()); //先弹出一个
while (j < nums.length) { //j经过前面的操作,数值已经是k了
helperQueue.enqueue(nums[j]); //弹入
helperQueue.dequeue(nums[i]);//弹出
resArr.push(helperQueue.front());//得出最大值
i++, j++;
}
return resArr;
};
队列,弹出一个,进一个,调用ju_max函数求最大值。结果超出时间限制。
- 自己想的时候,想把后面的也不要,只要一个最大数,不行;或者就把最大值放前。
2.直接构造一个数据结构
单调队列——其实队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队里里的元素数值是由大到小的。
347.前 K 个高频元素
法一:
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var topKFrequent = function(nums, k) {
const map = new Map()
nums.forEach(n => {
map.set(n, map.has(n) ? map.get(n)+1 : 1)
})
// 首先将字典转成数组,然后对数组中的第二个元素(频度)从小到大排序
const list = Array.from(map).sort((a, b) => b[1] - a[1])
// 截取频率前k高的元素
return list.slice(0, k).map(n => n[0])
};
map统计,然后排序输出
但实施遇到麻烦
// 首先将字典转成数组,然后对数组中的第二个元素(频度)从小到大排序
const list = Array.from(map).sort((a, b) => b[1] - a[1])
// 截取频率前k高的元素
return list.slice(0, k).map(n => n[0])
效果等于:
let arr2 = Array.from(m) //Map => Array
arr2.sort(function (a,b) {//按出现次数降序排列
return b[1]-a[1]
})
for (let i = 0; i < k; i++) {//返回前k个元素
rest.push(arr2[i][0])
}
return rest
参考1
参考2
map方法1
map方法2
法二:
// 没有堆 需要自己构造
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]);
}
}
const topKFrequent = function (nums, k) {
const map = new Map();
for (const num of nums) {
map.set(num, (map.get(num) || 0) + 1);
}
// 创建小顶堆
const heap= new Heap((a, b) => a[1] - b[1]);
// entry 是一个长度为2的数组,0位置存储key,1位置存储value
for (const entry of map.entries()) {
heap.push(entry);
if (heap.size() > k) {
heap.pop();
}
}
// return heap.queue.map(e => e[0]);
const res = [];
for (let i = heap.size() - 1; i >= 0; i--) {
res[i] = heap.pop()[0];
}
return res;
};
听视频思路听懂了,看js代码一头雾水。
法三:
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var topKFrequent = function(nums, k) {
//法三:哈希表+桶排序
let map = new Map();
for(let num of nums) {
map.set(num, map.has(num) ? map.get(num) + 1 : 1);//初始化出现次数为1,之后累加
}
if(k === map.size) return [...map.keys()];//k如果等于map.size,直接返回全部key
const bucketSort = () => {
let arr = [];
let res = [];
map.forEach((value, key) => {//arr[i]存放频率为i的key数组
if(!arr[value]) arr[value] = [key];
else arr[value].push(key);
});
for(let i = arr.length - 1; i >= 0 && res.length < k; i--) {
if(arr[i]) {
res.push(...arr[i]);//将数组转换为用逗号分割的参数序列
}
}
return res;
}
return bucketSort();
};
map.foreach解释
跟图形学边表桶的样式差不多
桶排序,将频率一致的放在同一个桶里,数组下标i放置频率为i的key,最后倒序取出k个key 因为可以按任意顺序返回答案,所以k如果等于map.size,那么直接返回全部key,不用排序 这两种都挺快的,但是法一sort时间复杂度为为O(nlogn),不符合题意.
桶排序最好的时间复杂度接近O(n)