代码随想录算法训练营第13天 | 239. 滑动窗口最大值 、347.前 K 个高频元素

67 阅读3分钟

 239. 滑动窗口最大值

思路:构建一个单调队列来做,保证最大值永远都在队列前面。那么如何构建呢?我们可以每次比较推入的元素与队列尾部元素进行比较,如何比他小则推入,如果比他大则将尾部元素从尾部推出来。

看代码随想录还有另外一种思路,但是那种比较难一点。二刷再看看

class MyQueue {
  Deque<Integer> deque = new LinkedList<>();
  //弹出元素时,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出
  //同时判断队列当前是否为空
  void poll(int val) {
      if (!deque.isEmpty() && val == deque.peek()) {
          deque.poll();
      }
  }
  //添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出
  //保证队列元素单调递减
  //比如此时队列元素3,1,2将要入队,比1大,所以1弹出,此时队列:3,2
  void add(int val) {
      while (!deque.isEmpty() && val > deque.getLast()) {
          deque.removeLast();
      }
      deque.add(val);
  }
  //队列队顶元素始终为最大值
  int peek() {
      return deque.peek();
  }
}

class Solution {
  public int[] maxSlidingWindow(int[] nums, int k) {
      if (nums.length == 1) {
          return nums;
      }
      int len = nums.length - k + 1;
      //存放结果元素的数组
      int[] res = new int[len];
      int num = 0;
      //自定义队列
      MyQueue myQueue = new MyQueue();
      //先将前k的元素放入队列
      for (int i = 0; i < k; i++) {
          myQueue.add(nums[i]);
      }
      res[num++] = myQueue.peek();
      for (int i = k; i < nums.length; i++) {
          //滑动窗口移除最前面的元素,移除是判断该元素是否放入队列
          myQueue.poll(nums[i - k]);
          //滑动窗口加入最后面的元素
          myQueue.add(nums[i]);
          //记录对应的最大值
          res[num++] = myQueue.peek();
      }
      return res;
  }
}

题目链接/文章讲解/视频讲解:programmercarl.com/0239.%E6%BB…

 347.前 K 个高频元素

【思路】:先将每一个元素遍历一遍,记录其个数到map集合中,然后遍历map集合,将每个key,value转换为集合存储到最小堆(优先级队列中),每次遍历都判断最小堆的大小有没有大于k,如果k则将最小堆中的最小元素弹出(这里其实就是保证最小堆的元素总和与k的值相等,到时候直接遍历最小堆则是所需要的前k个元素)

如果基于最大堆实现,则直接将map集合放在最大堆中,到时候弹出前k个元素即可,相当于排了一个序再取前k个元素。

class Solution {
  public int[] topKFrequent(int[] nums, int k) {
      // 优先级队列
      // lambda 表达式设置优先级队列从大到小存储 o1 - o2 为从小到大,o2 - o1 反之
      PriorityQueue<int[]> pq = new PriorityQueue<>((o1, o2) -> o1[1] - o2[1]);
      int[] res = new int[k]; // 答案数组为 k 个元素
      Map<Integer, Integer> map = new HashMap<>(); // 记录元素出现次数
      for(int num : nums) map.put(num, map.getOrDefault(num, 0) + 1);
      for(var x : map.entrySet()) { // entrySet 获取 k-v Set 集合
          // 将 kv 转化成数组
          int[] tmp = new int[2];
          tmp[0] = x.getKey();
          tmp[1] = x.getValue();
          pq.offer(tmp);
          // 下面的代码是根据小根堆实现的,我只保留优先队列的最后的k个,只要超出了k我就将最小的弹出,剩余的k个就是答案
          if(pq.size() > k) {
              pq.poll();
          }
      }
      for(int i = 0; i < k; i ++) {
          res[i] = pq.poll()[0]; // 获取优先队列里的元素
      }
      return res;
  }
}

题目链接/文章讲解/视频讲解:programmercarl.com/0347.%E5%89…

 总结 

栈:先进后出 队列:先进先出 两个都可以右双向队列实现

programmercarl.com/%E6%A0%88%E…