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

Day11 栈与队列part2

逆波兰表达式求值

150. 逆波兰表达式求值

力扣题目链接(opens new window)

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

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

说明:

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

示例 1:

  • 输入: [“2”, “1”, “+”, “3”, " * "]
  • 输出: 9
  • 解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
class Solution {
public:int evalRPN(vector<string>& tokens) {stack<long long> st;for(int i = 0; i < tokens.size(); i++){if (tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/"){//出现符号,出栈两个元素用于计算long long num1 = st.top();st.pop();long long num2 = st.top();st.pop();// 计算后入栈if (tokens[i] == "+") st.push(num2 + num1);if (tokens[i] == "-") st.push(num2 - num1);if (tokens[i] == "*") st.push(num2 * num1);if (tokens[i] == "/") st.push(num2 / num1);}else{st.push(stoll(tokens[i])); // stoll: str->long long}}return st.top();}
};
import operatorclass Solution:def evalRPN(self, tokens: List[str]) -> int:stack = []ops = {"+": operator.add,"-": operator.sub,"*": operator.mul,"/": lambda a,b : int(a/b)}for token in tokens:if token not in ops:stack.append(int(token))else:b = stack.pop()a = stack.pop()stack.append(ops[token](a, b))return stack.pop()

滑动窗口最大值

239. 滑动窗口最大值

力扣题目链接(opens new window)

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

返回滑动窗口中的最大值

这道题不能用优先级队列,大顶堆;因为这个窗口是移动的,而大顶堆每次只能弹出最大值,我们无法移除其他数值,这样就造成大顶堆维护的不是滑动窗口里面的数值了。

class Solution {
private:class MyQueue{public:deque<int> que;  // 使用deque来实现单调队列void pop(int value){/*每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。同时pop之前判断队列当前是否为空。假设数组是 [1, 3, 2],窗口大小 k=2。初始:push(1) → 队列 [1]右移一格:push(3) → 因为 3 > 1,把 1 弹掉,队列 [3]窗口左边的 1 要移出,但它不在队列里了(被挤掉了),所以 pop(1) 什么都不做。下一步:窗口 [3, 2] → push(2) → 队列 [3, 2]现在要移出 3,此时队头就是 3,所以 pop(3) 把它弹掉。这样队列才能始终保持和当前窗口匹配。*/if(!que.empty() && value == que.front()){que.pop_front();}}void push(int value){// 如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。// 这样就保持了队列里的数值是单调从大到小的了。while(!que.empty() && value > que.back()){que.pop_back();}que.push_back(value);}int front(){return que.front();}};
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {MyQueue que;vector<int> result;for (int i = 0; i < k; i++){que.push(nums[i]);}result.push_back(que.front());for (int i = k; i < nums.size(); i++){que.pop(nums[i-k]); // 滑动窗口移除最前面元素que.push(nums[i]); // 滑动窗口前加入最后面的元素result.push_back(que.front()); // 记录对应的最大值}return result;}
};

Python 的 collections.deque双端队列(double-ended queue)

方法说明
append(x)右端 加入元素(相当于尾巴)
appendleft(x)左端 加入元素(相当于头部)
pop()从右端 弹出元素(尾巴)
popleft()从左端 弹出元素(头部)

deque 并不是普通队列严格意义上的 FIFO,既可以当队列,也可以当栈来用

from collections import deque
class Solution:def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:max_list = []kept_nums = deque()for i in range(len(nums)):push_left(kept_nums, nums[i])if i >= k and nums[i-k] == kept_nums[0]:kept_nums.popleft()if i >= k-1:  // 最左面的即[0]是最大的max_list.append(kept_nums[0])return max_listdef push_left(kept_nums, num):while kept_nums and num > kept_nums[-1]:kept_nums.pop()kept_nums.append(num)

通过push_left函数我们可以发现,kept_nums相当于c++中的按引用传递了。

Python 没有传统意义上的“按值传递”和“按引用传递”,而是 “对象引用传递”(pass-by-object-reference / pass-by-assignment)

​ 核心思想

  • 传入函数的永远是对象的引用(类似指针)。
  • 可变对象(mutable,如 list、dict、set)在函数内被修改,函数外也会看到变化。
  • 不可变对象(immutable,如 int、str、tuple)在函数内修改会生成新对象,函数外不会变化。

C++ 有三种常用方式传递:

方式函数签名调用方式特点
值传递void f(int x)f(a)在函数内部复制,修改不影响外部
指针传递void f(int* p)f(&a)函数内部用 *p 修改,外部也会改变;需要手动解引用
引用传递void f(int& x)f(a)函数内部直接用 x 修改,外部也会改变;语法更简洁

前 K 个高频元素

347.前 K 个高频元素

力扣题目链接(opens new window)

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

示例 1:

  • 输入: nums = [1,1,1,2,2,3], k = 2
  • 输出: [1,2]

大/小顶堆的应用, 在C++中就是优先级队列

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

class Solution {
public:// 小顶堆class mycomparison {public:bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {return lhs.second > rhs.second;}};vector<int> topKFrequent(vector<int>& nums, int k) {// 要统计元素出现频率unordered_map<int, int> map;for (int i = 0; i < nums.size(); i++){map[nums[i]]++;}// 对频率排序// 定义一个小顶堆,大小为kpriority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;// 用固定大小为k的小顶堆,扫面所有频率的数值for (unordered_map<int, int>::iterator it = map.begin(); it != map.end(); it++) {pri_que.push(*it);if (pri_que.size() > k) { // 如果堆的大小大于了K,则队列弹出,保证堆的大小一直为kpri_que.pop();}}// 找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒序来输出到数组vector<int> result(k);for (int i = k - 1; i >= 0; i--) {result[i] = pri_que.top().first;pri_que.pop();}return result;}
};
class Solution:def topKFrequent(self, nums: List[int], k: int) -> List[int]:# 使用字典统计数字出现次数time_dict = defaultdict(int)for num in nums:time_dict[num] += 1# 更改字典,key为出现次数,value为相应的数字的集合index_dict = defaultdict(list)for key in time_dict:index_dict[time_dict[key]].append(key)# 排序key = list(index_dict.keys())key.sort()  # 升序result = []cnt = 0# 获取前k项while key and cnt <= k:result += index_dict[key[-1]]cnt += len(index_dict[key[-1]])key.pop()return result[0: k]
http://www.xdnf.cn/news/17925.html

相关文章:

  • duiLib 实现鼠标拖动状态栏时,窗口跟着拖动
  • webrtc弱网-VideoSendStreamImpl类源码分析与算法原理
  • 《Leetcode》-面试题-hot100-技巧
  • 嵌入式硬件篇---常见的单片机型号
  • 按键及消抖
  • Python环境下载安装、以及环境配置教程(Windows版)
  • java项目怎么实现用户行为分析、漏斗转化、数据可视化报表。
  • C语言零基础第18讲:自定义类型—结构体
  • 楼宇自控系统赋能建筑全维度管理,实现环境、安全与能耗全面监管
  • [Oracle数据库] Oracle 复杂查询
  • 当 GitHub 宕机时,我们如何协作?
  • Flink Sql 按分钟或日期统计数据量
  • 从 “视频孪生” 到 “视频动态目标三维重构”:技术演进与核心突破
  • PHP域名授权系统网站源码_授权管理工单系统_精美UI_附教程
  • 基于W55MH32Q-EVB 实现 HTTP 服务器配置 OLED 滚动显示信息
  • 企业级Java项目金融应用领域——银行系统
  • 【P40 6-3】OpenCV Python——图像融合(两张相同属性的图片按比例叠加),addWeighted()
  • B3924 [GESP202312 二级] 小杨的H字矩阵
  • Java后台生成多个Excel并用Zip打包下载
  • 《Python学习之字典(一):基础操作与核心用法》
  • 基于 EC 数据与大模型技术实现天气预报:从数据到上线的全栈方法
  • 学习嵌入式第三十天
  • C++进阶:IO流
  • 【Vibe Coding 工程之 StockAnalyzerPro 记录】- EP3.Phase 2股票列表管理功能
  • JCTools 无锁并发队列基础:ConcurrentCircularArrayQueue
  • TDengine IDMP 高级功能(4. 元素引用)
  • C# 反射和特性(关于应用特性的更多内容)
  • 解锁JavaScript性能优化:从理论到实战
  • C#WPF实战出真汁09--【消费开单】--选择菜品
  • 一次性能排查引发的Spring MVC深度思考