第五章 栈与队列part 03 239.滑动窗口最大值 347.前 K 个高频元素

第十三天| 第五章 栈与队列part 03 239.滑动窗口最大值 347.前 K 个高频元素

239.滑动窗口最大值(第一道困难题)

  • 题目链接:https://leetcode.cn/problems/sliding-window-maximum/

  • 文章讲解:https://programmercarl.com/0239.%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E6%9C%80%E5%A4%A7%E5%80%BC.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE

  • 题目介绍:给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

    • 返回 滑动窗口中的最大值 。
  • 解法:

    • 思路:大致的思路是这样,滑动窗口就类似于一个队列,每次移动的时候,丢弃的元素就poll掉,新加入的元素就offer进来,然后我每移动一次就get一下队列的maxValue。
      • 那我到底什么时候poll呢?怎么poll可以保证每次peek的时候就是最大值?
        • 这里存在两种移除的情况:
          • 第一种情况,为保证队列的出口处无时无刻都是最大值,在加入元素时,保证队列内元素单调递减。即只要前面有元素小于当前元素,就要移除。(这里判断当前元素和队列的最后一个元素即可保证上述逻辑)
          • 第二种情况,加入元素时,如果队列是满的(数量等于k),需要移除,移除的是哪一个呢?因为队列是满的,也就意味着前三个元素一直单调递减,即队列中的第一个元素是最大值。因此,当我们向后移动时,如果前三个元素中的第一个和队列出口处的元素相等,就移除这个元素。
          • 所以我们每次需要先进行判断,滑动窗口的前一个元素(下标为i - k的元素)是否要从队列中移除,再向队列中加入元素,再将出口处的最大值peek出来。
  • 代码:

    • 第一种:自定义单调队列

      • class Solution {
            public int[] maxSlidingWindow(int[] nums, int k) {
                MyQueue myQueue = new MyQueue();
                int[] res = new int[nums.length - k + 1];
                int count = 0;
                for (int i = 0; i < k; i++) {
                    myQueue.add(nums[i]);
                }
                res[count++] = myQueue.peek();
                for (int i = k; i < nums.length; i++) {
                    myQueue.poll(nums[i - k]);
                    myQueue.add(nums[i]);
                    res[count++] = myQueue.peek();
                }
                return res;
            }
        }
        
        class MyQueue {
            Deque<Integer> deque = new LinkedList<>();
            public void poll (int value) {
                if (!deque.isEmpty() && value == deque.peek()) {
                    deque.poll();
                }
            }
        
            public void add (int value){
                while (!deque.isEmpty() && value > deque.peek()) {
                    deque.removeLast();
                }
                deque.add(value);
            }
        
            public int peek() {
                return deque.peek();
            }
        }
        
    • 第二种:使用双端队列模拟单调队列

      • 这里的思路比较简单,就是定义一个双端队列来存数组的下标

      • i为nums下标,是要在[i - k + 1, i] 中选到最大值,只需要保证两点

        • 1.队列头结点需要在[i - k + 1, i]范围内,不符合则要弹出
        • 2.既然是单调,就要保证每次放进去的数字要比末尾的都大,否则也弹出
      • 最后,因为单调,当i增长到符合第一个k范围的时候,每滑动一步都将队列头节点放入结果就行了。

      • class Solution {
            public int[] maxSlidingWindow(int[] nums, int k) {
                Deque<Integer> deque = new ArrayDeque<>();
                int[] res = new int[nums.length - k + 1];
                int idx = 0;
                for (int i = 0; i < nums.length; i++) {
                    // deque中的元素要满足[i - k + 1, i]
                    while (!deque.isEmpty() && deque.peek() < i - k + 1) {
                        deque.pollFirst();
                    }
                    while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
                        deque.pollLast();
                    }
                    deque.offer(i);
                    if (i >= k - 1) {
                        res[idx++] = nums[deque.peek()];
                    }
                }
                return res;
            }
        }
        

347.前 K 个高频元素

  • 题目链接:https://leetcode.cn/problems/top-k-frequent-elements/

  • 文章讲解:https://programmercarl.com/0347.%E5%89%8DK%E4%B8%AA%E9%AB%98%E9%A2%91%E5%85%83%E7%B4%A0.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE

  • 题目介绍:给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

    • 示例 1:

      输入: nums = [1,1,1,2,2,3], k = 2
      输出: [1,2]
      
  • 解法:

    • 思路:采用优先级队列的小顶堆方式
  • 代码:

    • class Solution {
          public int[] topKFrequent(int[] nums, int k) {
              Map<Integer, Integer> map = new HashMap<>();
              for (int num : nums) {
                  map.put(num, map.getOrDefault(num, 0) + 1);
              }
              PriorityQueue<int[]> pq = new PriorityQueue<>((pair1, pair2) -> pair1[1] - pair2[1]);
              for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
                  if (pq.size() < k) {
                      pq.add(new int[]{entry.getKey(), entry.getValue()});
                  } else {
                      if (entry.getValue() > pq.peek()[1]) {
                          pq.poll();
                          pq.add(new int[]{entry.getKey(), entry.getValue()});
                      }
                  }
              }
              int[] res = new int[k];
              for (int i = 0; i < k; i++) {
                  res[i] = pq.poll()[0];
              }
              return res;
          }
      }
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值