代码随想录训练营第十一天 | 150. 逆波兰表达式求值 239. 滑动窗口最大值 347.前 K 个高频元素

150. 逆波兰表达式求值

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> st = new Stack<>();
        int i1, i2, res;
        for(String s : tokens){
            if("+".equals(s)){
                i2 = st.pop();
                i1 = st.pop();
                st.push(i1+i2);
            }else if("-".equals(s)){
                i2 = st.pop();
                i1 = st.pop();
                st.push(i1-i2);
            }else if("*".equals(s)){
                i2 = st.pop();
                i1 = st.pop();
                st.push(i1*i2);
            }else if("/".equals(s)){
                i2 = st.pop();
                i1 = st.pop();
                st.push(i1/i2);
            }else{
                st.push(Integer.valueOf(s));
            }
        }
        return st.pop();
    }
}

239. 滑动窗口最大值 (难:单调队列,不同于优先级队列)

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

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

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

一开始的思路,维护一个最大值int max;,但是一旦最大值poll之后,就不知道此时窗口内的最大值是多少了。
难道要重新遍历一遍?用两个队列?写不下去……

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        LinkedList<Integer> q = new LinkedList<>();
        int[] res = new int[nums.length - k + 1];
        int max = Integer.MIN_VALUE;
        for(int i=0; i<k; i++){
            q.offer(nums[i]);
            max = max>nums[i]?max:nums[i];
        }
        res[0] = max;
        for(int i=1; i<nums.length - k + 1; i++){
            if(max)
        }
    }
}

使用单调队列

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        // Deque<Integer> q = new LinkedList<>();
        Que q = new Que();
        int[] res = new int[nums.length-k+1];
        // 初始化
        int start=0, end=k-1; // 左闭右闭
        for(int i=start; i<=end; i++) q.offer(nums[i]);
        res[start] = q.peek();
        // 窗口开始滑动
        start++; end++;
        for(; end<nums.length; start++, end++){ // 先出队还是先入队? 先入队,保证队列里会有元素。
            // 入队
            q.offer(nums[end]);
            
            // 出队
            q.poll(nums[start-1]);

            // 记录最大值
            res[start] = q.peek();
        }
        return res;
    }

    private static class Que{
        private Deque<Integer> q = new LinkedList<>();

        public void offer(int i){
            while(!q.isEmpty()){
                if(q.peekLast()>=i){
                    q.offer(i);
                    break;
                }
                q.pollLast();
            }
            if(q.isEmpty()){
                q.offer(i);
            }
        }

        public Integer poll(int i){
            if(q.peek()==i){
                return q.poll();
            }
            return null;
        }

        public Integer peek(){
            return q.peek();
        }

        public Integer peekLast(){
            return q.peekLast();
        }

        public boolean isEmpty(){
            return q.isEmpty();
        }
    }
}

代码随想录解法:

  1. add更加简洁:将while中的if判断放在while中;
  2. poll更合理:我的poll耦合度太高,并未考虑队列中没有元素的情况。
//解法一
//自定义数组
class MyQueue {
    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) {
        if (nums.length == 1) {
            return nums;
        }
        int len = nums.length - k + 1;
        //存放结果元素的数组
        int[] res = new int[len];
        int num = 0;
        //自定义队列
        MyQueue myQueue = new MyQueue();
        //先将前k的元素放入队列
        for (int i = 0; i < k; i++) {
            myQueue.add(nums[i]);
        }
        res[num++] = myQueue.peek();
        for (int i = k; i < nums.length; i++) {
            //滑动窗口移除最前面的元素,移除是判断该元素是否放入队列
            myQueue.poll(nums[i - k]);
            //滑动窗口加入最后面的元素
            myQueue.add(nums[i]);
            //记录对应的最大值
            res[num++] = myQueue.peek();
        }
        return res;
    }
}

347.前 K 个高频元素 (难:优先级队列)

大顶堆(Max Heap)和小顶堆(Min Heap)是堆的两种主要形式,它们的主要区别在于堆的性质(即堆序性质)不同。堆是一种特殊的完全二叉树,通常用数组来实现。
大顶堆是一种特殊的完全二叉树,其中每个节点的值都大于或等于其子节点的值。因此,大顶堆的根节点(堆顶)是所有节点中最大的。
小顶堆是一种特殊的完全二叉树,其中每个节点的值都小于或等于其子节点的值。因此,小顶堆的根节点(堆顶)是所有节点中最小的。


如果使用大顶堆

  1. PriorityQueue需要传入比较器
  2. Map.Entry
class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> record = new HashMap<>();
        for(int i:nums){
            record.put(i, record.getOrDefault(i, 0)+1);
        }
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b)->(b[1]-a[1]));
        for(Map.Entry<Integer, Integer> entry : record.entrySet()){
            pq.offer(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;
    }
}

本题使用小顶堆:因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。

总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值