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

C++算法训练营 Day11 栈与队列(2)

1.逆波兰表达式求值

给你一个字符串数组tokens,表示一个根据逆波兰表示法表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:

有效的算符为 '+''-''*' '/'
每个操作数(运算对象)都可以是一个整数或者另一个表达式。
两个整数之间的除法总是 向零截断 。
表达式中不含除零运算。
输入是一个根据逆波兰表示法表示的算术表达式。
答案及所有中间计算结果可以用32 位整数表示。

示例 1:

输入:tokens = [“2”,“1”,“+”,“3”,“*”]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) *3) = 9

示例 2:

输入:tokens = [“4”,“13”,“5”,“/”,“+”]
输出:6 解释:该算式转化为常见的中缀算术表达式为:(4 + (13/ 5)) = 6

示例 3:

输入:tokens = [“10”,“6”,“9”,“3”,“+”,“-11”,““,”/“,””,“17”,“+”,“5”,“+”]
输出:22
解释:该算式转化为常见的中缀算术表达式为: ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

  • 解题思路:

逆波兰表达式即后缀表达式,其特点为:
1.运算符在操作数之后
2.无需括号即可明确运算顺序
3.适合栈结构计算

(1)创建存储操作数的栈。

// 使用 long long 类型栈防止溢出stack<long long> st;

(2)然后遍历 token所有值,若遇到操作数,就进行压栈处理;若遇到运算符,就进行弹出栈中所有操作数,然后进行运算,这里要注意:先弹出的操作数是后一项,即被减数或被除数

 // 遍历所有 tokenfor (int i = 0; i < tokens.size(); i++) {// 当前 token 是运算符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);   // 加法else if (tokens[i] == "-") st.push(num2 - num1);   // 减法(注意操作数顺序)else if (tokens[i] == "*") st.push(num2 * num1);   // 乘法else if (tokens[i] == "/") st.push(num2 / num1);   // 除法(注意操作数顺序)}// 当前 token 是操作数else {// 将字符串转换为 long long 并压栈st.push(stoll(tokens[i]));  // stoll = string to long long}}

(3)遍历完成后,获取栈中操作数的值,对于栈中操作数弹不弹出都没影响。

// 获取最终结果long long result = st.top();st.pop();  // 清空栈(可选操作)return result;  // 返回整型结果

完整代码如下:

class Solution {
public:int evalRPN(vector<string>& tokens) {// 使用 long long 类型栈防止溢出stack<long long> st;// 遍历所有 tokenfor (int i = 0; i < tokens.size(); i++) {// 当前 token 是运算符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);   // 加法else if (tokens[i] == "-") st.push(num2 - num1);   // 减法(注意操作数顺序)else if (tokens[i] == "*") st.push(num2 * num1);   // 乘法else if (tokens[i] == "/") st.push(num2 / num1);   // 除法(注意操作数顺序)}// 当前 token 是操作数else {// 将字符串转换为 long long 并压栈st.push(stoll(tokens[i]));  // stoll = string to long long}}// 获取最终结果long long result = st.top();st.pop();  // 清空栈(可选操作)return result;  // 返回整型结果}
};

2.滑动窗口最大值

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

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

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
在这里插入图片描述

示例 2:

输入:nums = [1], k = 1
输出:[1]

  • 解题思路

本题利用到单调队列,所谓单调队列就是:维护一个元素值单调递减的双端队列(队头到队尾递减),队头元素始终是当前窗口的最大值,从而高效获取滑动窗口的最大值,避免每次遍历窗口。

(1)先初始化,创建单调队列
(2)填充初始窗口:将前k个元素加入队列,并记录初始最大值,即获取队列头部元素。
(3)滑动窗口:先移除窗口左侧元素(如果它仍是当前最大值),然后添加窗口右侧新元素(维护队列单调性),最后获取当前窗口最大值。
(4)返回结果。

class Solution {
private:// 自定义单调队列类class MyQueue {public:deque<int> que; // 双端队列作为底层存储// 移除元素(仅在元素等于队首时移除)void pop(int value) {// 只有当队列非空且队首等于要移除的值时才弹出if (!que.empty() && value == que.front()) {que.pop_front();}}// 添加元素(维护单调递减特性)void push(int value) {// 移除比当前值小的所有尾部元素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; // 存储结果的向量// 填充初始窗口(前k个元素)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;}
};

3.前 K 个高频元素

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

示例 1:

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

示例 2:

输入: nums = [1], k = 1
输出: [1]

  • 解题思路:

执行过程示例
假设输入:nums = [1,1,1,2,2,3], k = 2

步骤1:统计频率
元素: 1 → 频率: 3
元素: 2 → 频率: 2
元素: 3 → 频率: 1

步骤2:构建小顶堆(维护K=2)
添加 [1:3] → 堆: [1:3]
添加 [2:2] → 堆: [2:2] [1:3](堆顶是2:2)
添加 [3:1] → 堆: [3:1] [1:3] [2:2] → 堆大小>2 → 弹出堆顶(3:1)
最终堆: [2:2] [1:3](堆顶是2:2)

步骤3:提取结果
倒序输出:
i=1: 堆顶2 → 结果[1]=2
i=0: 堆顶1 → 结果[0]=1
最终结果: [1,2]

class Solution {
public://自定义比较器类(小顶堆)class mycomparison {public://重载 () 运算符,用于比较两个 pairbool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {//比较频率值:lhs.second > rhs.second 返回 true//这样在优先队列中,频率较小的元素会被放在堆顶return lhs.second > rhs.second;}};vector<int> topKFrequent(vector<int>& nums, int k) {// 1.统计元素出现频率unordered_map<int, int> map; //存储<元素, 出现次数>for (int i = 0; i < nums.size(); i++) {map[nums[i]]++; //统计每个元素的出现次数}// 2.创建小顶堆存储 Top K 元素//参数说明://- pair<int, int>: 存储<元素, 频率>//- vector<pair<int, int>>: 底层容器//- mycomparison: 自定义比较器priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;//3.遍历频率表,维护大小为 K 的小顶堆for (auto it = map.begin(); it != map.end(); it++) {pri_que.push(*it); //将当前元素加入堆//堆大小超过 K 时,移除频率最小的元素if (pri_que.size() > k) {pri_que.pop(); //弹出堆顶(当前最小频率元素)}}//4.提取结果(倒序输出)vector<int> result(k);for (int i = k - 1; i >= 0; i--) {result[i] = pri_que.top().first; //获取元素值pri_que.pop(); //移除已处理元素}return result;}
};

相关文章:

  • [文献阅读] Emo-VITS - An Emotion Speech Synthesis Method Based on VITS
  • C++.OpenGL (12/64)光照贴图(Lightmaps)
  • 【E9批量执行SQL】
  • 【论文解读】Toolformer: 语言模型自学使用工具
  • 金融预测模型开发:数据预处理、机器学习预测与交易策略优化
  • 复旦联合百度发布Hallo4:让AI肖像“活”起来!新型扩散框架实现高保真音频驱动动画生成!
  • 轻量高效的B站视频下载解析
  • 【CSS-5】深入理解CSS复合选择器:提升样式表的精确性与效率
  • React Hooks 示例项目
  • 大话软工笔记—需求调研概述
  • 探针有哪些?探测方法有哪些?
  • 一个完整的日志收集方案:Elasticsearch + Logstash + Kibana+Filebeat (二)
  • vue3+dify从零手撸AI对话系统
  • 深入剖析MySQL存储架构,索引结构,日志机制,事务提交流程
  • Sklearn 机器学习 缺失值处理 填充数据列的缺失值
  • Oracle 客户端深度指南:SQL Developer 与 PL/SQL Developer 全面安装使用教程
  • SpringAI Alibaba实战文生图
  • Python Day45
  • LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
  • 量子计算突破:新型超导芯片重构计算范式
  • 新闻网站建设研究现状/成都品牌推广
  • 百度网站名称和网址/一份完整的市场调查方案
  • 什么网站排名做的最好/百度金融
  • 百度浏览器下载/seo站长网
  • 建湖哪家专业做网站/无锡今日头条新闻
  • 大连网站专业制作/真正免费的网站建站平台推荐