LeetCode刷题日记之栈与队列III

86 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情 1.滑动窗口的最大值

题目描述

image.png

解题思路

1.我们自定义一个单调递减队列,存放可能是最大值的元素。
2.当移动滑动窗口时,如果出滑动窗口时的元素正好等于队列对头元素,把该元素从队列移除,如果进入滑动窗口的元素把队列头元素大,那么移除队列所有元素,再把该元素入队列。
3.滑动窗口移动时,把每个队列头元素加入结果数组中。

var maxSlidingWindow = function(nums, k) {
   class Dequeue {
       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 helperDequeue = new Dequeue()
   let i = 0,j =0;
   let result = []
   while(j<k){
       helperDequeue.enqueue(nums[j++])
   }
   result.push(helperDequeue.front());
    while (j < nums.length) {
        helperDequeue.enqueue(nums[j]);
        helperDequeue.dequeue(nums[i]);
        result.push(helperDequeue.front());
        i++, j++;
    }
    return result;
};

2.前k个高频元素

题目描述

image.png

解题思路

1.使用map统计每个数字出现的次数。
2.理解大顶堆小顶堆的含义:堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。
3.因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。

var topKFrequent = function(nums, k) {
  const map = new Map();
  
  for(const num of nums) {
    map.set(num, (map.get(num) || 0) + 1);
  }

  // 创建小顶堆
  const priorityQueue = new PriorityQueue((a, b) => a[1] - b[1]);

  // entry 是一个长度为2的数组,0位置存储key,1位置存储value
  for (const entry of map.entries()) {
    priorityQueue.push(entry);
    if (priorityQueue.size() > k) {
      priorityQueue.pop();
    }
   }

   const ret = [];

   for(let i = priorityQueue.size() - 1; i >= 0; i--) {
     ret[i] = priorityQueue.pop()[0];
   }

   return ret;
};

class PriorityQueue {
    constructor(compareFn){
        this.queue = []
        this.compareFn = compareFn
    }


    push(item){
        this.queue.push(item)
        let index = this.queue.length - 1
        let parent = Math.floor((index - 1) / 2)

        // 上浮
        while(parent>=0&&this.compare(parent,index)>0){
            [this.queue[index],this.queue[parent]] = [this.queue[parent],this.queue[index]]
            index = parent
            parent = Math.floor((index-1)/2)
        }
    }

    pop(){
        const ret = this.queue[0]
        this.queue[0] = this.queue.pop()
        let index = 0;
        let left = 1;
        let  selectedChild  = this.compare(left,left+1)>0?left+1:left

         // 下沉
       while(selectedChild !== undefined && this.compare(index, selectedChild) > 0) {
       // 交换
          [this.queue[index], this.queue[selectedChild]] =
           [this.queue[selectedChild], this.queue[index]];
          index = selectedChild;
          left = 2 * index + 1;
          selectedChild = this.compare(left, left + 1) > 0 ? left + 1 : left;
        }

        return ret;
    }

    size(){
        return this.queue.length
    }

    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]);
    }
}