代码随想录第十三天 | 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());
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?