当前位置: 首页 > news >正文

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

题目:150. 逆波兰表达式求值

根据逆波兰表示法,求表达式的值。

有效的运算符包括 +-*/。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

说明

  • 整数除法只保留整数部分。
  • 给定逆波兰表达式总是有效的,即表达式总能得出有效数值且不存在除数为 0 的情况。

解题思路

逆波兰表达式的特点是操作数在前,运算符在后,计算时从左到右扫描,并在遇到运算符时进行计算。因此可以使用栈来辅助计算:

  1. 使用栈存储数字
    • 遍历表达式中的每个元素,遇到数字则入栈。
    • 遇到运算符时,弹出栈顶的两个数字进行计算,将结果入栈。
  2. 逆序操作
    • 因为 num2 - num1num2 / num1 的顺序要求,我们需注意先弹出的数字为右操作数,第二个弹出的是左操作数。
  3. 最终结果
    • 扫描完表达式后,栈中只剩一个元素,即为计算结果。

反思

  1. 栈的使用:栈的后进先出特性很适合逆波兰表达式的求值。每次遇到运算符时取出两个操作数,可以实现正确的运算顺序。
  2. 操作符的判断:在Java中需要使用 .equals() 来判断字符串是否相等。需要特别注意 "/""-" 的运算顺序。
  3. 边界处理:因为题目保证表达式有效,因此不用处理异常情况,如除零、非法字符等。

代码

class Solution {public int evalRPN(String[] tokens) {Deque<Integer> nums = new ArrayDeque<>();for(int i=0;i<tokens.length;i++){if (tokens[i].equals("+")) {int num1=nums.pop();int num2=nums.pop();nums.push(num1+num2);} else if (tokens[i].equals("-")) {int num1=nums.pop();int num2=nums.pop();nums.push(num2-num1);} else if (tokens[i].equals("*")) {int num1=nums.pop();int num2=nums.pop();nums.push(num2*num1);}else if (tokens[i].equals("/")) {int num1=nums.pop();int num2=nums.pop();nums.push(num2/num1);}else {nums.push(Integer.parseInt(tokens[i]));}}return nums.pop();}
}

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

给定一个整数数组 nums 和一个大小为 k 的滑动窗口,该窗口从数组的最左侧移动到最右侧。每次只能看到滑动窗口内的 k 个数字,并且滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。


解题思路

可以使用双端队列 deque 来实现滑动窗口最大值的计算。deque 维护当前滑动窗口的最大元素的索引,保证队列的第一个元素为当前窗口的最大值的索引:

  1. 维护双端队列:对于每个新元素,检查队列的有效性,移除不在窗口范围内的元素(索引小于当前窗口的左边界)。
  2. 移除较小元素:对于即将加入窗口的元素,移除队列尾部比当前元素小的所有元素,确保队列从前到后保持一个递减的顺序。
  3. 窗口最大值:每次当索引超过窗口大小时,将队列头部的元素索引对应的值作为窗口的最大值记录到结果数组中。

反思

  1. 双端队列的优势:利用双端队列,可以在 O(n) 的时间复杂度内完成滑动窗口最大值的求解。每个元素最多进出队列一次,效率极高。
  2. 边界检查:在 poll 方法中,用 if 语句代替 while,可以减少不必要的重复检查并提高代码可读性。
  3. 优化添加和移除操作:在每次移除和添加元素时,利用 deque 的头部和尾部操作保证队列内容和窗口范围的一致性。

代码

class MyQueue{Deque<Integer> deque = new LinkedList<>();void poll(int val){if(!deque.isEmpty() && val==deque.peek()){deque.poll();}}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];MyQueue myQueue = new MyQueue();int num = 0;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;}
}
class Solution {public int[] maxSlidingWindow(int[] nums, int k) {ArrayDeque<Integer> deque = new ArrayDeque<>();int n = nums.length;int[] res = new int[n - k + 1];int idx=0;for(int i=0;i<nums.length;i++){while(!deque.isEmpty() && deque.peek() < i-k+1){deque.poll();}while(!deque.isEmpty() && nums[deque.peekLast()] < nums[i]){deque.pollLast();}deque.offer(i);if(i>=k-1){res[idx++] =  nums[deque.peek()];}}return res;}
}

347.前 K 个高频元素

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

给定一个非空的整数数组 nums,返回其中出现频率前 k 高的元素。


解题步骤

  1. 构建频率哈希表:遍历数组 nums,记录每个元素出现的频率,使用 HashMap 存储元素及其对应的频率。
  2. 维护大小为 k 的优先队列:利用优先队列(堆)保存频率最高的前 k 个元素。
    • 大顶堆:直接将所有元素放入堆中,然后弹出前 k 个元素。
    • 小顶堆:逐步添加元素至堆中,超过 k 个时删除堆顶最小元素。堆中最后剩下的 k 个元素即为频率前 k 高的元素。
  3. 结果输出:将优先队列中的元素依次出队,存入结果数组。

反思

  1. 优先队列(堆)应用:使用小顶堆在存储容量为 k 的情况下实现频率筛选,从而减少时间复杂度。
  2. 空间优化:在大数据量下,利用 PriorityQueue 实现频率最高的前 k 个元素筛选,有效避免对整个数据集排序。
  3. 边界条件:注意当 k 大于 nums 中不同元素个数时,可能出现特殊情况。

class Solution {public int[] topKFrequent(int[] nums, int k) {Map<Integer,Integer> map = new HashMap<>();for(int num:nums){map.put(num,map.getOrDefault(num,0)+1);}PriorityQueue<int[]> pq = new PriorityQueue<>((pair1,pair2) -> pair2[1] - pair1[1]);for(Map.Entry<Integer,Integer> entry : map.entrySet()){pq.add(new int[]{entry.getKey(),entry.getValue()});}int[] ans = new int[k];for(int i =0;i<k;i++){ans[i] = pq.poll()[0];}return ans;}
}
class Solution {public int[] topKFrequent(int[] nums, int k) {// 优先级队列,为了避免复杂 api 操作,pq 存储数组// lambda 表达式设置优先级队列从大到小存储 o1 - o2 为从小到大,o2 - o1 反之PriorityQueue<int[]> pq = new PriorityQueue<>((o1, o2) -> o1[1] - o2[1]);int[] res = new int[k]; // 答案数组为 k 个元素Map<Integer, Integer> map = new HashMap<>(); // 记录元素出现次数for (int num : nums) map.put(num, map.getOrDefault(num, 0) + 1);for (var x : map.entrySet()) { // entrySet 获取 k-v Set 集合// 将 kv 转化成数组int[] tmp = new int[2];tmp[0] = x.getKey();tmp[1] = x.getValue();pq.offer(tmp);// 下面的代码是根据小根堆实现的,我只保留优先队列的最后的k个,只要超出了k我就将最小的弹出,剩余的k个就是答案if(pq.size() > k) {pq.poll();}}for (int i = 0; i < k; i++) {res[i] = pq.poll()[0]; // 获取优先队列里的元素}return res;}
}

http://www.mrgr.cn/news/59069.html

相关文章:

  • Python自动化测试+邮件推送+企业微信推送+Jenkins
  • 单臂交换知识点
  • P1496 火烧赤壁
  • IDEA关联Tomcat——最新版本IDEA 2024
  • 大数据-190 Elasticsearch - ELK 日志分析实战 - 配置启动 Filebeat Logstash
  • Spring Boot技术中小企业设备管理系统设计与实践
  • qml圆形图片,qml圆形头像制作
  • 家人们,做小红书/小绿书一定要学会蹭热点啊
  • 移植picocom到hisi平台上
  • PDF怎么编辑修改内容?这份PDF编辑器全攻略请收好!
  • 清理pip和conda缓存
  • linux驱动_platform总线是如何注册的
  • Android——事件冲突处理
  • springboot083基于springboot的个人理财系统--论文pf(论文+源码)_kaic
  • 一文彻底理解 JavaScript 解构赋值
  • 当前读和快照读有什么区别?
  • Python自动化会议记录与摘要生成
  • 现在设备普遍切换成TYPE-C适配器后,一拖三数据线接口变革探析
  • 软考高级架构-7.1-软件架构概念-超详细讲解+精简笔记
  • 机器人转人工时,开启实时质检(mod_cti基于FreeSWITCH)
  • Kaggle 数据集dogs-vs-cats的错误
  • 真的有免费的MC/Terraria/...服务器?简幻欢让你实现开服梦!
  • Mysql使用pt工具在大表添加索引
  • JAVA入门知识点小结-day4
  • 【jvm】所有的线程都共享堆吗
  • 使用pytest单元测试框架执行单元测试