算法训练营Day13|栈与队列part3

239. 滑动窗口最大值 【单调队列】

  • 自我尝试
    • 思路: 使用queue来维系窗口,每个窗口里再进行流排序去max操作。这个暴力法明显超时了。
    • 代码
      class Solution {
          public int[] maxSlidingWindow(int[] nums, int k) {
              Deque<Integer> q = new ArrayDeque<>();
              int[] result = new int[nums.length-k+1];
              for(int i=0; i<nums.length;i++){
                  q.add(nums[i]);// 保持两个,进去到三个
                  if(q.size()==k){
                      result[i-k+1]=q.stream().mapToInt(Integer::intValue).max().getAsInt();
                      q.poll();
                  }
                  
              }
              return result;
              
          }
      }
    • 收获
      • stream:流操作可以串联一起形成一个流水线,每个操作将从其前一个操作中接收数据、处理数据,并将结果发送到下一个操作。操作分为两类:中间与终端操作。
        • 中间操作: 操作返回一个新的流,将中间操作串联在一起。例如mapToInt()是一个中间操作,将其转为Intstream流。
          • 一般来说就是那个传入函数参数的方法为中间操作。例如map(String::valueOf)
        • 终端操作: 终端操作完成流处理并生成结果。它不返回流,而是返回非流值,一旦执行终端操作流就不能再被使用。max()返回最大值就是一个终端操作。
          • 返回值类,例如.max().getAsInt()返回int值。
          • 返回集合类,例如.collect(Collectors.joining())
  • 范例后的自我尝试
    • 思路:实现一个单调队列,通过自我定义的push,pop,以及getMaxValue方法来实现期许的结果。
      • 主函数思路:通过遍历整个nums,调用三个定义的操作来获取最后的结果。
        • headsup1:push操作每次都要执行。
        • headsup2:pop操作只在元素积累到k+1个时候才开始pop。
        • headsup3:result的添加需要在元素积累到k个时候就开始。
      • push函数思路:当队列里尾部元素小于当前元素时候不断移除,随后添加进去。
        • headsup1:当待加元素等于尾部元素时候,也添加进入队列。这样的好处是在pop的时候方便移除。
      • pop函数思路:当传入的元素值与队列头元素相同时候移除
        • headsup1:这个传入元素是哪一个要清楚!
      • getMaxValue函数思路: 直接调用deque自带的peekFirst()获取
    • 代码
      class Solution {
          public int[] maxSlidingWindow(int[] nums, int k) {
              Deque<Integer> q = new ArrayDeque<>();
              int[] result = new int[nums.length-k+1];
              for(int i=0; i<nums.length;i++){
                  push(nums[i],q);
                  // System.out.println("--- "+i +"th round ---");
                  // System.out.println(q.toString());
                  if(i-k>=0){
                      pop(nums[i-k],q);
                      // System.out.println(q.toString());
                  }
                  
                  if(i-k+1>=0){
                      result[i-k+1]=getMaxValue(q);
                  }
              }
              return result;
          }
          public void push(int val, Deque<Integer> q){
              //在push的时候pop掉小于等于val的元素,维护队列的单调性
              while(!q.isEmpty()&&q.getLast()<val){
                  q.pollLast();
              }
              q.add(val);
          }
          public void pop(int val, Deque<Integer> q){
              //当被遍历的元素是queue出口端的元素时候,才pop
              if(val == q.getFirst()){
                  q.pollFirst();
              }
          }
          public int getMaxValue(Deque<Integer> q){
              return q.peekFirst();
          }
              
      }
    • 收获
      • Deque使用方法:
        • 添加元素: offerFirst(E e)/ offerLast(E e);【注:若无法插入返回false】
        • 检索头/尾元素: peekFirst()/peekLast(); 【注:若队列为空返回null】
        • 移除元素: pollFirst()/pollLast();【注:若队列为空返回null】

347.前 K 个高频元素  【优先级队列】

  • 范例题解
    • 思路: 通过map来获取频率后,通过一个小顶堆的priorityQueue来维护住k个元素,即通过与堆顶相比,更小的一方出局。最终留下的k个,再传到数组里作为返回值。
    • 代码
      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]);
              //遍历map,添加队列元素
              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()});
                      }
                  }
              }
      
              //传递最后值来return
              int[] ans = new int[k];
              for(int i=k-1;i>=0; i--){
                  ans[i] = pq.poll()[0];
              }
              return ans;
             
          }
      }
    • 收获:
      • priorityQueue:一个数据类型,它支持插入新元素和按照优先级删除元素的操作。
        • 创建:PriorityQueue<int[]> pq = new PriorityQueue<>((pair1,pair2)->pair1[1]-pair2[1]);
          • 这里的lambda表达式定义优先级队列中元素的排序方式。它返回了一个comparator对象。
          • 如果pair1[1]-pair2[1]<0,结果为负数,表明第一个对象应该排在第二个对象之前。
          • 相反如果为正数,第一个要排在第二个之后。
        • 与堆的关系:
          • 堆是实现优先队列的常用办法。通过堆,在o(logn)的时间里完成优先级队列的插入和删除。
      • Entry与entrySet的关系:
        • Entry是java.util.map的一个接口,每个entry对象包含一个key与一个value,可通过getKey()与getValue()来获得
        • "Map.Entry<CLASSA,CLASSB>" 提供了方便的方式来访问它们
        • entrySet也是java.util.map的一个方法,它返回一个包含所有entry的set集合。
      • 堆:
        • 结构:完全二叉树,高度为logn。最后一层从左到右排布node
        • 操作:
          • 插入:O(logn):将元素放在堆尾,然后上浮到合适位置,最多logn次。
          • 查询:O(1):一般只查根节点。
          • 删除:O(logn):将底层最后一个元素放到root,然后下沉恢复堆的属性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值