代码随想录算法训练营day11 | 150.逆波兰表达式求值、239.滑动窗口最大值、347.前K个高频元素
150.逆波兰表达式求值
栈的应用:后缀表达式求值
点击查看代码
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> stk;
for(int i = 0; i < tokens.size(); ++i) {
if(tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/") {
int ropnum = stk.top();
stk.pop();
int lopnum = stk.top();
stk.pop();
if(tokens[i] == "+") stk.push(lopnum + ropnum);
else if(tokens[i] == "-") stk.push(lopnum - ropnum);
else if(tokens[i] == "*") stk.push(lopnum * ropnum);
else stk.push(lopnum / ropnum);
}
else {
int weight = 1;
int sum = 0;
//将表示数字的字符串转为真正的数字int,存在sum中,再压栈
for(int j = tokens[i].size() - 1; j >= 0; --j) {
//注意有可能遇到负数的情况
if(j == 0 && tokens[i][j] == '-') sum *= -1;
else {
sum += (tokens[i][j] - '0') * weight;
weight *= 10;
}
}
stk.push(sum);
}
}
return stk.top();
}
};
逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。
该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。
逆波兰表达式主要有以下两个优点:
1、去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
2、适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中。
239.滑动窗口最大值
解法一:暴力解法(结果超时)
点击查看代码
class Solution {
public:
int maxValue(vector<int> &nums, int left, int right) {
int maxval = nums[left];
for(int i = left + 1; i <= right; ++i) maxval = max(maxval, nums[i]);
return maxval;
}
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int left = 0;
int right = left + k - 1;
vector<int> result;
while(right <= nums.size() - 1) {
result.push_back(maxValue(nums, left, right));
++left;
++right;
}
return result;
}
};
解法二:使用单调队列(难)
点击查看代码
class Solution {
private:
//实现单调队列,队头元素为当前窗口内的最大值,队内元素由大到小排列,仅保留有可能成为最大值的元素
class MyQueue {
private:
deque<int> dque;
public:
//小于等于待push元素x的元素,由于较小且比起x来说会更早滑出窗口,故后续不可能成为最大值,可直接pop
//x需要插入到第一个比x大的值后面,等待前面的值均滑出窗口后,x就可以成为新的窗口内最大值,并且,排在
//后面的元素必然是后入队的,必然会更晚滑出窗口
void push(int x) { //x为即将滑入窗口的值
//注意此处< x不能写成<= x,相同的值需要随着窗口滑动轮流成为最大值,并且随着pop自行淘汰
//若写成<= x,该用例会报错[-7,-8,7,5,7,1,6,0]
//预期为[7,7,7,7,7] 结果错误为[7,7,7,6,6]
while(!dque.empty() && dque.back() < x) dque.pop_back();
dque.push_back(x);
}
//x要么小于队头元素,要么等于,不可能大于,因为队头元素就是当前窗口最大值
//若x等于队头元素,即当前窗口最大值,直接弹出,x后面的元素成为新的窗口最大值,
//不用担心x之后元素不在新的窗口内,因为x后的元素必然是后入队的,必然会更晚滑出窗口
void pop(int x) { //x为即将滑出窗口的值
if(x == dque.front()) dque.pop_front();
}
int front() {
return dque.front();
}
};
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
MyQueue myque;
for(int i = 0; i < k; ++i) {
myque.push(nums[i]);
}
vector<int> result;
for(int i = k; i < nums.size(); ++i) {
result.push_back(myque.front());
myque.pop(nums[i-k]);
myque.push(nums[i]);
}
result.push_back(myque.front());
return result;
}
};
347.前K个高频元素(难)
使用优先级队列priority_queue,即大/小顶堆,队头元素即为堆顶元素
点击查看代码
class Solution {
public:
//自定义比较规则,用于传入priority_queue
class myCompare {
public:
bool operator()(const pair<int, int> &left, const pair<int, int> &right) {
//相当于队头在右边,右边小,则为小顶堆
return left.second > right.second;
}
};
vector<int> topKFrequent(vector<int>& nums, int k) {
//利用map统计每个元素的频率
unordered_map<int, int> uomap;
for(int i = 0; i < nums.size(); ++i)
++uomap[nums[i]];
//构建小顶堆,对map里的元素进行排序
//堆中元素类型 底层容器类型 排序规则
priority_queue<pair<int, int>, vector<pair<int, int>>, myCompare> pri_que;
for(auto iter = uomap.begin(); iter != uomap.end(); ++iter) {
pri_que.push(*iter);
if(pri_que.size() > k)
pri_que.pop(); //让堆始终保持k个最大的
}
//将筛选后的堆中元素的first域逐个放入用于返回的vector中
vector<int> result(k);
for(int i = k - 1; i >= 0; --i) {
result[i] = pri_que.top().first;
pri_que.pop();
}
return result;
}
};
2025/02/23
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性