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

第一题150. 逆波兰表达式求值

根据 逆波兰表示法,求表达式的值。
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
注意 两个整数之间的除法只保留整数部分。
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

ψ(`∇´)ψ 我的思路

  • 题目上提示的已经很清晰了

去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中

package stackandqueue;

import java.util.Stack;

public class EvalRPN {

    public int evalRPN(String[] tokens) {
        int m,n;
        Stack<String> stack = new Stack<>();
        for (int i = 0; i < tokens.length; i++) {
            //如果是运算符的话,取出栈顶的两个元素,和运算符计算,结果入栈
            if(tokens[i].equals("+")){
                stack.push(String.valueOf(Integer.parseInt(stack.pop())+Integer.parseInt(stack.pop())));
            } else if (tokens[i].equals("-")) {
                m = Integer.parseInt(stack.pop());
                n = Integer.parseInt(stack.pop());
                stack.push(String.valueOf(n-m));
            } else if (tokens[i].equals("*")) {
                stack.push(String.valueOf(Integer.parseInt(stack.pop())*Integer.parseInt(stack.pop())));
            }else if (tokens[i].equals("/")) {
                m = Integer.parseInt(stack.pop());
                n = Integer.parseInt(stack.pop());
                stack.push(String.valueOf(n/m));
            } else {
                stack.push(tokens[i]);//如果不是运算符的话,入栈
            }
        }
        return Integer.parseInt(stack.pop());
    }
}

时间复杂度O(n)

  • 在力扣上debug也太方便了吧😀



第二题239. 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值

ψ(`∇´)ψ 我的思路

  • 暴力暴力!一个for循环确定滑动窗口的位置,另一个for循环确定滑动窗口内的最大值
package stackandqueue;

public class MaxSlidingWindow {

    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums.length==1){
            return nums;
        }
        int[] res = new int[nums.length - k + 1];//结果数组
        for (int i = 0; i < nums.length - k + 1; i++) {
            // 确定滑动窗口的位置[i,i+k-1]
            int x = Integer.MIN_VALUE;
            for (int j = i; j < i + k; j++) {
                // 遍历滑动窗口内的值,寻找最大值
                x = nums[j] > x ? nums[j] : x;
            }
            res[i] = x;
        }
        return res;
    }
}

时间复杂度O(n^2)

  • 然后就。。。超时了😅

  • 就很乖的去看了正经解法🙂

package stackandqueue;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedList;

public class MaxSlidingWindow2 {

    static  Deque<Integer> que = new LinkedList<>();
    public static int[] maxSlidingWindow(int[] nums, int k) {
        int[] res = new int[nums.length - k + 1];
        for (int i = 0; i < k; i++) {
            push(nums[i]);
        }
        res[0] = getMax();
        for (int i = 1; i < nums.length - k + 1; i++) {
            pop(nums[i-1]);//弹出元素
            push(nums[i + k-1]);//加入元素
            res[i] = getMax();//取最大值
        }
        return res;
    }
    public static void pop(int x) {
        if (!que.isEmpty() && que.peek() == x) {
            //如果队列不为空,并且队列出口的值=要弹出的值
            que.pop();
        }
    }
    public static void push(int x) {
       while (!que.isEmpty()&&x>que.getLast()){
           que.removeLast();//当队列不空且要加入的元素大于队列最后一个元素时,把最后的元素去掉(保证队列倒序)
        }
        que.addLast(x);//在把元素追加至队尾

    }
    public static int getMax() {
        // 最大值一直是队列出口的元素
        return (int) que.getFirst();
    }

    public static void main(String[] args) {
        int[] ints = maxSlidingWindow(new int[]{1,3,1,2,0,5}, 3);
        for (int i = 0; i < ints.length; i++) {
            System.out.println(ints[i]);
        }
    }
}

时间复杂度O(n)

  • 尽管听了思路,实现起来还是有点复杂的,关键是push的时候要从队尾把元素移除,再加入元素,这样可以保证队列里的元素倒序。




第三题347.前 K 个高频元素

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

ψ(`∇´)ψ 我的思路

  • 这题我想着是用map,出现的次数为key,出现的元素为value,for循环一次遍历将数组存入map,对key排序,取前k个返回其value。今天不太舒服就不写了,直接看代码随想录的新方法。
package stackandqueue;

import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;

public class TopKFrequent {

    public int[] topKFrequent(int[] nums, int k) {

        // 先把数组中的元素装入map
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            if (!map.containsKey(nums[i])) {
                map.put(nums[i], 1);
            } else {
                map.put(nums[i],map.get(nums[i])+1);
            }
        }
        PriorityQueue<int[]> pq = new PriorityQueue<>((pair1, pair2)->pair1[1]-pair2[1]);
        for(Map.Entry<Integer,Integer> entry:map.entrySet()){//小顶堆只需要维持k个元素有序
            if(pq.size()<k){//小顶堆元素个数小于k个时直接加
                pq.add(new int[]{entry.getKey(),entry.getValue()});
            }else{
                if(entry.getValue()>pq.peek()[1]){//当前元素出现次数大于小顶堆的根结点(这k个元素中出现次数最少的那个)
                    pq.poll();//弹出队头(小顶堆的根结点),即把堆里出现次数最少的那个删除,留下的就是出现次数多的了
                    pq.add(new int[]{entry.getKey(),entry.getValue()});
                }
            }
        }
        int[] ans = new int[k];
        for(int i=k-1;i>=0;i--){//依次弹出小顶堆,先弹出的是堆的根,出现次数少,后面弹出的出现次数多
            ans[i] = pq.poll()[0];
        }
        return ans;
    }
}
  • 思路听了下,但并不是很理解PriorityQueue这个东西,感觉我对队列的理解还是差点儿



总结


  • 基本可以用栈来解题,但是队列我还是没有完全弄明白,大顶堆小顶堆也只是懂一点儿,先欠下来,等6号,7号的时候再做个总结。

  • 今天终于做到了一道困难题,还是很开心的,期待二叉树

补充

队列

  • 实现类挺多的,一般用LinkedList

  • 堆是一个完全二叉树
  • 大顶堆:头部为堆中最大的值
  • 小顶堆:头部为队中最小的值
  • PriorityQueue:一个具有优先级的队列,该优先级使得队列始终按照自然顺序进行排序,队列的头部为最小值。

构造小顶堆:
PriorityQueue small=new PriorityQueue<>();

构造大顶堆:
PriorityQueue small=new PriorityQueue<>(Collections.reverseOrder());

posted @ 2022-10-03 16:31  nnn~  阅读(43)  评论(0编辑  收藏  举报