代码随想录算法Day10|(栈与队列part2)150. 逆波兰表达式求值 、239. 滑动窗口最大值、347.前 K 个高频元素

目录

150. 逆波兰表达式求值

解题分析

本题代码

239. 滑动窗口最大值

解题分析

本题代码

347.前 K 个高频元素

解题分析

本题代码


150. 逆波兰表达式求值

题目链接:https://leetcode.cn/problems/evaluate-reverse-polish-notation/description/

文章讲解:https://programmercarl.com/0150.%E9%80%86%E6%B3%A2%E5%85%B0%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%B1%82%E5%80%BC.html

解题分析

逆波兰表达式相当于二叉树的后续遍历,可直接用栈来顺序处理:

  • 遇到数字,入栈
  • 遇到运算符,取出栈顶两个数字进行计算,并将结果压入栈中

所有运算结束弹出栈的数字作为返回值。

本题代码

class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new LinkedList();
        for(String s : tokens){
            if("+".equals(s)){    //遇到运算符,注意使用equals()比较
                stack.push(stack.pop() + stack.pop());
            }else if("-".equals(s)){
                stack.push(-stack.pop() + stack.pop());
            }else if("*".equals(s)){
                stack.push(stack.pop() * stack.pop());
            }else if("/".equals(s)){
                int temp1 = stack.pop();
                int temp2 = stack.pop();
                stack.push(temp2 / temp1);
            }else{     //遇到数字
                stack.push(Integer.valueOf(s));  //String转成Integer类型:Integer.valueOf()
            }
        }
        return stack.pop();
    }
}

239. 滑动窗口最大值

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

文章讲解: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

解题分析

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

比如:对于窗口里的元素{2, 3, 5, 1 ,4},单调队列里只维护{5, 4} 就够了,保持单调队列里单调递减,此时队列出口元素就是窗口里最大元素。

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

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

代码思路:

  • 可创建一个MyQueue类,创建队列,并包含add()、poll()、peek()方法;
  • 另一个Solution类进行调用。创建滑动窗口队列和返回数组:

                滑动窗口队列前k个元素直接添加,找到返回数组的第一个值,然后开始滑动,进行add()、poll()的操作,并调用peek()获取每次的最大值赋给返回数组。

本题代码

class MyQueue{
    Deque<Integer> deque = new LinkedList<>();
    public void add(int value){
        while(!deque.isEmpty() && value > deque.getLast()){   //注意这里是while循环,getLast()是获取队列最后一个元素
            deque.removeLast();
        }
        deque.add(value);
    }

    public void poll(int value){
        if(!deque.isEmpty() && value == deque.peek()){
            deque.poll();   //无参
        }
    }

    public int peek(){
        return deque.peek();
    }
}

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int len = nums.length - k + 1;
        int[] res = new int[len];  //注意滑动窗口的长度
        int num = 0;    //需创建一个int变量,记录返回数组的角标
        MyQueue myQueue = new MyQueue();

        for(int i = 0; i < k; i++){   //先将前k个元素放入滑动窗口
            myQueue.add(nums[i]);
        }
        res[num++] = myQueue.peek();

        for(int i = k; i < nums.length; i++){   //开始滑动
            myQueue.poll(nums[i-k]);  //注意是i-k
            myQueue.add(nums[i]);
            res[num++] = myQueue.peek();
        }

        return res;
    }
}

347.前 K 个高频元素

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

文章讲解:https://programmercarl.com/0347.%E5%89%8DK%E4%B8%AA%E9%AB%98%E9%A2%91%E5%85%83%E7%B4%A0.html

解题分析

这道题目主要涉及到如下三块内容:

  1. 统计元素出现频率:使用map来进行统计
  2. 对频率排序:用小顶堆,因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素
  3. 找出前K个高频元素

代码思路:

1.统计元素出现频率:

        创建map,记录数组每个元素(key),及出现频率(value)

2.对频率排序:

        1)实现小顶堆,元素类型为长度为2数组,放元素和元素出现次数

              实现方式——创建优先级队列,实现Comparator接口,数值小的在前

        2)将map的键值对放入队列中:

  • 小顶堆个数小于k时,直接放入
  • 小顶堆个数大于k时,比较map键值对的value与小顶堆栈顶数组第二个元素,若栈顶的小,则弹出栈顶,加入大的

3.找出前K个高频元素:

        创建返回数组,长度为k的一维数组;遍历小顶堆,将每个数组的首个元素(即元素本身)放入数组

        因为小顶堆栈顶是最小数,所以倒叙遍历小顶堆

本题代码

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        //1.创建map,记录数组每个元素(key),及出现频率(value)
        Map<Integer,Integer> map = new HashMap<>();
        for(int num: nums){
            map.put(num,map.getOrDefault(num,0) + 1);
        }

        //2.实现小顶堆,元素类型为长度为2数组,放元素和元素出现次数
        //实现方式——创建优先级队列,实现Comparator接口,数值小的在前
        PriorityQueue<int[]> pq = new PriorityQueue<>(new Comparator<int[]>(){
            public int compare(int[] m, int[] n){
                return m[1] - n[1];
            }
        });

        //3.将map的键值对放入队列中
        //1)小顶堆个数小于k时,直接放入
        //2)小顶堆个数大于k时,比较map键值对的value与小顶堆栈顶数组第二个元素,若栈顶的小,则弹出栈顶,加入大的
        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()});
                }
            }
        }

        //4.创建返回数组,长度为k的一维数组;遍历小顶堆,将每个数组的首个元素(即元素本身)放入数组
        //因为小顶堆栈顶是最小数,所以倒叙遍历小顶堆
        int[] res = new int[k];
        for(int i = k-1; i >= 0; i--){
            res[i] = pq.poll()[0];
        }

        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值