代码随想录第11天 | 239. 滑动窗口最大值 347.前 K 个高频元素

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函数求最大值。结果超出时间限制。


思想

  • 设置一个队列,这个队列呢,放进去窗口里的元素,然后随着窗口的移动,队列也一进一出,每次移动之后,队列告诉我们里面的最大值是什么。
  • enqueue(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
  • dequeue(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止
  • 放入value,比value小的都弹出。
    如5 2 -1,放入0——就是5 2 0。
    放入-2——5 2 -1
    放入 6——6
    放入3 ——5 3

困难

  • 思想困难
  1. 自己想的时候,想把后面的也不要,只要一个最大数,不行;或者就把最大值放前。

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统计,然后排序输出
但实施遇到麻烦

debug图

代码随想录第11天 | 239. 滑动窗口最大值 347.前 K 个高频元素_第1张图片


收获

    // 首先将字典转成数组,然后对数组中的第二个元素(频度)从小到大排序
    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 
  • map转数组

参考1
参考2

  • 数组的map方法

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)

你可能感兴趣的:(代码随想录,javascript,leetcode)