代码随想录算法训练营第十三天|239. 滑动窗口最大值、 Leetcode347. 前 K 个高频元素、栈和队列总结。

239. 滑动窗口最大值 (主要理解思路)

题目链接:239. 滑动窗口最大值

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;
        //自定义队列
        MyDeque myDeque = new MyDeque();
        //先将前k个元素放入队列
        for(int i =0;i<k;i++){
   
            myDeque.add(nums[i]);
        }
        res[num++] = myDeque.peek();
        for(int i=k;i<nums.length;i++){
   
            //滑动窗口移除最前面的元素,移除是判断该元素是否放入队列
            myDeque.poll(nums[i-k]);
            //滑动窗口加入最后面的元素
            myDeque.add(nums[i]);
            //记录对应的最大值
            res[num++] = myDeque.peek();
        }
        return res; 
    }
}
class MyDeque{
   
    //解法一:自定义数组
    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) {
   
        //解法二:
        //利用双端队列手动实现单调队列
        /**
        用一个单调队列来存储对应的下标,每当窗口滑动的时候,直接取队列的头部指针对应的值放入结果集即可
        单调队列类似(tail-->)3-->2-->10(-->head)(右边为头节点,元素存的是下标)
         */
        ArrayDeque<Integer> deque = new ArrayDeque<>();
        int n = nums.length;
        int[] res = new int[n-k+1];
        int idx = 0;
        for(int i =0;i<n;i++){
   
            //根据题意,i为nums下标,是要在[i-k+1,i]中选到最大值,只需要保证两点
            //1. 队列头节点需要在[i-k+1,i]范围内,不符合则要弹出
            while(!deque.isEmpty()&&deque.peek()<i-k+1){
   
                deque.poll();
            }
            //2.既然是单调,就要保证每次放进去的数字比末尾的都大,否则也要弹出
            while(!deque.isEmpty()&&nums[deque.peekLast()]<nums[i]){
   
                deque.pollLast();
            }
            deque.offer(i);

            //因为单调,当i增长到符合第一个k范围的时候,每滑动一步,都将队列头节点放入结果就行
            if(i>=k-1){
   
                res[idx++]=nums[deque.peek()];
            }
        }
        return res;
    }
}

347. 前 K 个高频元素(主要理解思路)

题目链接:347. 前 K 个高频元素

class Solution {
   
    public int[] topKFrequent(int[] nums, int k) {
   
        int[] result = new int[k];
        HashMap<Integer,Integer> map = new HashMap<>();
        for(int num:nums){
   
            map.put(num,map.getOrDefault(num,0)+1);
        }

        Set<Map.Entry<Integer,Integer>> entries = map.entrySet();
        PriorityQueue<Map.Entry<Integer,Integer>> queue = new PriorityQueue<>((o1,o2)->o2.getValue()-o1.getValue());
        for(Map.Entry<Integer,Integer> entry:entries){
   
            queue.offer(entry);
        }
        for(int i=k-1;i>=0;i--){
   
            result[i]=queue.poll().getKey();
        }
        return result;
    }
}

栈经典题目

括号匹配

建议要写代码之前要分析好有哪几种不匹配的情况,如果不动手之前分析好,写出的代码也会有很多问题。

先来分析一下 这里有三种不匹配的情况,

第一种情况,字符串里左方向的括号多余了,所以不匹配。
第二种情况,括号没有多余,但是括号的类型没有匹配上。
第三种情况,字符串里右方向的括号多余了,所以不匹配。
这里还有一些技巧,在匹配左括号的时候,右括号先入栈,就只需要比较当前元素和栈顶相不相等就可以了,比左括号先入栈代码实现要简单的多了!

字符串去重问题

思路就是可以把字符串顺序放到一个栈中,然后如果相同的话 栈就弹出,这样最后栈里剩下的元素都是相邻不相同的元素了。

逆波兰表达式问题

本题中每一个子表达式要得出一个结果,然后拿这个结果再进行运算,那么这岂不就是一个相邻字符串消除的过程,和栈与队列:匹配问题都是栈的强项 (opens new window)中的对对碰游戏是不是就非常像了。

队列的经典题目

滑动窗口最大值问题

这道题目还是比较绕的,如果第一次遇到这种题目,需要反复琢磨琢磨

主要思想是队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。

那么这个维护元素单调递减的队列就叫做单调队列,即单调递减或单调递增的队列。C++中没有直接支持单调队列,需要我们自己来一个单调队列

而且不要以为实现的单调队列就是 对窗口里面的数进行排序,如果排序的话,那和优先级队列又有什么区别了呢。

设计单调队列的时候,pop,和push操作要保持如下规则:

pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
push(value):如果push的元素value大于入口元素的数值,那么就将队列出口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止

保持如上规则,每次窗口移动的时候,只要问que.front()就可以返回当前窗口的最大值。

单调队列,首先要明确的是,题解中单调队列里的pop和push接口,仅适用于本题。
单调队列不是一成不变的,而是不同场景不同写法,总之要保证队列里单调递减或递增的原则,所以叫做单调队列。

求前 K 个高频元素

通过求前 K 个高频元素,引出另一种队列就是优先级队列。

什么是优先级队列呢?

其实就是一个披着队列外衣的堆,因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。

而且优先级队列内部元素是自动依照元素的权值排列。那么它是如何有序排列的呢?

缺省情况下priority_queue利用max-heap(大顶堆)完成对元素的排序,这个大顶堆是以vector为表现形式的complete binary tree(完全二叉树)。

什么是堆呢?

堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。

所以大家经常说的大顶堆(堆头是最大元素),小顶堆(堆头是最小元素),如果懒得自己实现的话,就直接用priority_queue(优先级队列)就可以了,底层实现都是一样的,从小到大排就是小顶堆,从大到小排就是大顶堆。

本题就要使用优先级队列来对部分频率进行排序。 注意这里是对部分数据进行排序而不需要对所有数据排序!

所以排序的过程的时间复杂度是 O ( log ⁡ k ) O(\log k) O(logk),整个算法的时间复杂度是O(n\log k)。

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-02-06 05:06:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-02-06 05:06:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-02-06 05:06:01       82 阅读
  4. Python语言-面向对象

    2024-02-06 05:06:01       91 阅读

热门阅读

  1. 初始化整形数组,后从小到大冒泡排序

    2024-02-06 05:06:01       50 阅读
  2. 基于单片机的LED显示系统

    2024-02-06 05:06:01       52 阅读
  3. 蓝桥杯刷题--python-2

    2024-02-06 05:06:01       48 阅读
  4. 实习记录——第十三天

    2024-02-06 05:06:01       52 阅读
  5. 【C/C++ 15】C++11右值引用

    2024-02-06 05:06:01       51 阅读
  6. 机器学习小结

    2024-02-06 05:06:01       54 阅读
  7. 机器学习本科课程 实验5 贝叶斯分类

    2024-02-06 05:06:01       42 阅读
  8. 嵌入式C语言学习——基于Linux与GCC

    2024-02-06 05:06:01       55 阅读
  9. React中的Props:传递数据与组件通信

    2024-02-06 05:06:01       57 阅读