算法练习day11

53 阅读1分钟

一、滑动窗口最大值

问题要点

通过长度为窗口大小的单调递减队列,保证队尾为队列的最大值

push的思路:如果入队的元素大于队首的元素,则不断出队,知道遇到大于当前元素

pop的思路:如果窗口要移除的元素等于单调队列的队尾元素,则队列弹出元素,否则队列不操作

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */
var maxSlidingWindow = function(nums, k) {
    let queue = []
    let result = []

    for(let i = 0; i < nums.length; i++) {
        if(i >= k && nums[i-k] === queue[0]) {
            queue.shift()
        }
        while(queue.length && queue[queue.length - 1] < nums[i]) {
            queue.pop()
        }
        queue.push(nums[i])
        if(i >= k - 1) {
            result.push(queue[0])
        }
    }
    return result
};

二、前K个高频元素

问题要点

先要构造大小为k的小顶堆,然后把剩余的元素从k位置开始,跟堆顶元素比较,如果比堆顶大,则移除堆顶,把该元素放到堆顶,进行headAdjust,调整为小顶堆,最终剩下来的小顶堆就是top k的元素

class HeapQueue {
    queue = []
    map = null
    constructor(queue, map) {
        this.queue = queue
        this.map = map
    }
    heapify(k) {
        for (let i = Math.floor((k - 1) / 2); i >= 0; i--) {
            this.heapAdjust(i, k - 1)
        }
    }
    heapAdjust(index, end) {
        let left = 2 * index + 1
        let right = left + 1
        while (left <= end) {
            let target = index;
            if (this.map.get(this.queue[left]) < this.map.get(this.queue[target])) {
                target = left
            }
            if (right <= end && this.map.get(this.queue[right]) < this.map.get(this.queue[target])) {
                target = right
            }
            if (target === index) {
                break
            }
            [this.queue[target], this.queue[index]] = [this.queue[index], this.queue[target]];
            index = target
            left = 2 * index + 1
            right = left + 1
        }
    }
    heappop() {
        let size = this.queue.length
        [this.queue[0], this.queue[size - 1]] = [this.queue[size - 1], this.queue[0]];
        let top = this.queue.pop()
        this.heapAdjust(0, size - 2)
        return top
    }
    heappush(val) {
        this.queue.push(val)
        let size = this.queue.length
        let i = size - 1
        while (Math.floor((i - 1) / 2) >= 0) {
            let curRoot = Math.floor((i - 1) / 2)
            if (this.map.get(this.queue[curRoot]) < this.map.get(this.queue[i])) {
                break
            }
            nums[i] = nums[curRoot]
            i = curRoot
        }
    }
}

function topKFrequent(nums, k) {
    let map = new Map
    for (const item of nums) {
        map.set(item, (map.get(item) || 0) + 1)
    }
    let arr = [...new Set(nums)]
    let heapQueue = new HeapQueue(arr, map)
    heapQueue.heapify(k)
    for (let i = k; i < heapQueue.queue.length; i++) {
        if (map.get(heapQueue.queue[i]) > map.get(heapQueue.queue[0])) {
            [heapQueue.queue[i], heapQueue.queue[0]] = [heapQueue.queue[0], heapQueue.queue[i]];
            heapQueue.heapAdjust(0, k - 1)
        }
    }
    return heapQueue.queue.slice(0, k)
}