代码随想录Day13 | 239. 滑动窗口最大值,347. 前 K 个高频元素

本文记录了一周编程中的学习过程,涉及LeetCode题目解决,重点讲述了滑动窗口最大值问题的单调队列解法以及如何通过优先级队列处理前K个高频元素。作者在解决问题过程中遇到困难,通过视频和自我学习深化了对Deque和HashMap的理解。
摘要由CSDN通过智能技术生成

Day13

前言

新的一周又开始了,提高效率,继续加油

文章:代码随想录

视频:算法公开课-跟着Carl学算法

LeetCode 239 滑动窗口最大值

自己思路

看了看题目,只想得到暴力思路,听从建议,先看视频

看完讲解

看视频看的云里雾里很糊,感觉没有表述清楚,快看完了这一集才发现原来所谓的弹出和卷出去是两个意思,太口语化了,没懂是两个方向的弹出元素,看老半天看不懂,后面重新看了下文字版的讲解和评论区,感觉清晰多了,可以看看这个视频前半段的讲解和动画,清晰一些【单调队列 滑动窗口最大值【基础算法精讲 27】】

简单的概括就是单调队列,维护一个单调队列来对窗口进行管理,单调队列具有加入元素、弹出元素、获取最大值三个功能,窗口每移动一次,就调用这三个功能。加入元素,每加入一个元素,就和队列中的前一个元素(靠近入口的)进行比较,如果比他大,那么就把前一个元素弹出,一直比较到前一个元素大于自己了,再加入自己。弹出元素,每移动一次窗口,就将窗口漏去的值和靠近出口的元素进行比较,如果相同就弹出该元素,如果不同就不进行任何操作。这样下来,就保证了这个队列是单调的,最大的元素永远在出口处,所以最后的获取最大值功能,直接返回出口处元素即可。现在思路清晰了,但是代码写起来并不容易,所以是边看边写的,今天对Deque的总结也花了很多时间,gpt有时候和傻子一样

首先是要实现自己的单调队列类,牢记入口为Last,出口为First。首先需要注意的是,当获取栈内元素的时候,要在前面加上非空才能去获取。然后在push判断的时候,val大于当前值的时候才poll,等于的时候不poll,如果等于也poll会出现在push的时候poll一次,然后poll判断又相等了,会多poll一次从而出错。然后在Solution类中,记得是去实现这个单调队列类,返回的数组长度可以求出来,用index来控制该数组的索引。然后第一遍for循环是指加入第一组k个元素,此时i指的滑动窗口的起点,只需要进行add最后再peek就行。第二遍for循环i指的就是滑动窗口的终点了,每一次都依次执行poll、add、peek操作。最后循环结束了返回整个数组就行

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int[] result = new int[nums.length - k + 1];
        int index = 0;
        MyQueue myqueue = new MyQueue();
        for (int i = 0; i < k; i++) {
            myqueue.add(nums[i]);
        }
        result[index] = myqueue.peek();
        index++;
        for (int i = k; i < nums.length; i++) {
            myqueue.poll(nums[i - k]);
            myqueue.add(nums[i]);
            result[index] = myqueue.peek();
            index++;
        }
        return result;
    }
}

class MyQueue {
    Deque<Integer> deque = new LinkedList<>();
    public void poll(int val) {
        if (!deque.isEmpty() && deque.peekFirst() == val) {
            deque.pollFirst();
        }
    }
    public void add(int val) {
        while(!deque.isEmpty() && val > deque.peekLast()) {
            deque.pollLast();
        }
        deque.addLast(val);
    }
    public int peek() {
        return deque.peekFirst();
    }
}

LeetCode 347 前 K 个高频元素

自己思路

没有一个比较好的思路,去看视频

看完讲解

主要的思路就是采用优先级队列小顶堆,整个过程中遇到了非常多不懂的,恶补了很多知识,最后基本上是边看答案边写出来的,很多语言都不熟练,但是思路基本清楚了。我认为关键的就是两步,第一步是统计频率,这里借用HashMap。第二步就是维护前K个高频元素,这里利用的优先级队列的小顶堆,因为小顶堆每次都是弹出堆顶的最小的元素,这样子剩下的就是较大的元素了,所以用小顶堆,只需要保证小顶堆的元素是k个即可,如果不足k个直接加入,如果多于k个,就和最小元素比较,比他大那就加入,并且优先级队列PriorityQueue的add方法还会直接将元素添加到适当位置,就非常完美,所以关于是否需要add我们就只需要和最小的来比较就行,后面的就交给小顶堆自己维护

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        // 利用HashMap统计频率
        Map<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            map.put(num, map.getOrDefault(num,0)+1);
        }

        // 利用小顶堆维护k个元素(弹出的都是堆顶的小元素,留下的就是大的高频元素)
        PriorityQueue<int[]> pq = new PriorityQueue<>((pair1, pair2) -> pair1[1] - pair2[1]);
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            // 不足k个直接加
            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[] result = new int[k];
        for (int i = k-1 ; i >= 0; i--) {
            // 堆顶元素频率最低,排在后面
            result[i] = pq.poll()[0];
        }
        return result;
    }
}

总结

用时:近8h,遇到难题就感觉看视频很糊,恶补很多知识盲区

今天花了很多时间又对Deuqe进一步探索了,更新到Day10了;还研究了一下HashMap,更新到Day6了;其余和本节更相关的内容更新在下面了

增强for循环(for-each循环)

可以用来遍历任何实现了Iterable接口的对象,包括所有的Collection框架(如数组、集合,HashSet等),简化遍历过程

// 遍历数组
int[] arr = {1, 2, 3, 4, 5};
for(int num : arr) {
    System.out.println(num);
}

优先级队列(大顶堆/小顶堆)的定义和操作

优先级队列其实就是一个披着队列外衣的堆,所以优先级队列的操作和队列的操作就基本一致

但是关于为什么这样定义就是小顶堆,那样定义就是大顶堆,这个Lambad表达式还是不懂

// 优先级队列(大顶堆)的定义
PriorityQueue<int[]> pq = new PriorityQueue<>((pair1, pair2)->pair2[1]-pair1[1]);
// 优先级队列(小顶堆)的定义
PriorityQueue<int[]> pq = new PriorityQueue<>((pair1,pair2)->pair1[1]-pair2[1]);
 
// 优先级队列加入元素——根据优先级添加到适当的位置
pq.add(x);
 
// 优先级队列移除并返回优先级最高的元素
pq.poll();
 
// 优先级队列返回优先级最高的元素
pq.peek();
 
// 队列大小
pq.size();
 
// 队列空否
pq.isEmpty();

  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值