「代码随想录算法训练营」第十天 | 栈与队列 part2
150. 逆波兰表达式求值
题目链接:https://leetcode.cn/problems/evaluate-reverse-polish-notation/
题目难度:中等
文章讲解:https://programmercarl.com/0150.逆波兰表达式求值.html
视频讲解:https://www.bilibili.com/video/BV1kd4y1o7on
题目状态:多次修改 bug 后通过
个人思路:
通过栈进行解决。
先判断队列中的元素是否为数字类型。若为数字类型,压入栈;若不是数字类型,则肯定是运算符,使用switch
结构进行运算,将栈中的头两个元素弹出,进行运算符运算,运算后将结果在压入栈中。最后返回栈顶元素。
修改 bug 的过程:
- 栈的空间:当栈内有两个或两个以上的元素的时候才能进行运算符运算。
- 符号判断:在判断队列元素是否为数字的时候,忘记还有负数这一项,需要先判断元素的第一个元素是否为
-
且元素是否含有多个元素,若是,则从元素中的下一个元素判断是否为数字;若不是,则直接判断。
代码实现:
class Solution {
public:
bool isNum(const string &s) {
if(s[0] == '-' && s.size() > 1) {
for(int i = 1; i < s.size(); ++i) {
if(!isdigit(s[i])) return false;
}
} else if(s[0] == '-' && s.size() == 1) {
return false;
} else {
for(auto &elem: s) {
if(!isdigit(elem)) return false;
}
}
return !s.empty();
}
int evalRPN(vector<string>& tokens) {
stack<int> st;
for(auto &token: tokens) {
if(isNum(token)) {
st.push(stoi(token));
}
else if(!st.empty() && st.size() >= 2){
int op1 = st.top(); st.pop();
int op2 = st.top(); st.pop();
int res = 0;
switch(token[0]) {
case '+': res = op2 + op1; break;
case '-': res = op2 - op1; break;
case '*': res = op2 * op1; break;
case '/': res = op2 / op1; break;
}
st.push(res);
}
}
return st.top();
}
};
239. 滑动窗口最大值
题目链接:https://leetcode.cn/problems/sliding-window-maximum/
题目难度:困难
文章讲解:https://programmercarl.com/0239.滑动窗口最大值.html
视频讲解:https://www.bilibili.com/video/BV1XS4y1p7qj
题目状态:有思路,但败在了超时上,学习到了单调队列
思路:
一开始想,啊?困难题就这?根本难不倒我,最后却在编译的过程中败在了超时上,而且毫无思路去优化,最后学习了单调数列,利用单调数列解决这个问题异常轻松。
先看动图理解以下思路:
单调队列的构建:
- 首先创建一个
MyQue
类来实现单调函数,分别有三个函数:pop()
、push()
、front()
; push()
:这个不是简单的 push,当单调队列中存在的元素小于要 push 的元素,就需要将它们都弹出队列(使用pop_back()
,因为从队列的back
开始遍历的),而把要 push 的元素 push 进来。此时,单调队列中的队头就是 push 进来的最大元素。pop()
:当要弹出队列元素的时候,要弹出的元素小于队头元素时,表明这个元素早已在 push 的时候已经被弹出队列,不需要进行任何操作,只有当弹出元素和队头元素相等的时候,才需要把队头元素弹出(使用pop_front()
,因为队头在队列的front
处)。front()
:这个就是返回单调队列的队头元素,也是此时队列中最大的一个元素。
通过构建的单调队列实现滑动窗口中的最大值,窗口每滑动一次,就需要把对应的左边元素从单调队列中pop
出来,在把对应的右边的元素push
到单调队列中去。每滑动一次,就使用一个数组去记录单调队列中的队头元素(因为这个元素时此时滑动窗口中的最大值),最后返回数组即可。
代码实现:
class Solution {
public:
class MyQueue {
public:
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();
}
private:
deque<int> _que;
};
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
MyQueue mq;
vector<int> res;
for(int i = 0; i < k; ++i) {
mq.push(nums[i]);
}
res.push_back(mq.front());
for(int i = k; i < nums.size(); ++i) {
mq.pop(nums[i - k]);
mq.push(nums[i]);
res.push_back(mq.front());
}
return res;
}
};
347. 前 K 个高频元素
题目链接:https://leetcode.cn/problems/top-k-frequent-elements/
题目难度:中等
文章讲解:https://programmercarl.com/0347.前K个高频元素.html
视频讲解:https://www.bilibili.com/video/BV1Xg41167Lz
题目状态:有思路,通过 ChatGPT 优化代码后通过
个人思路:
创建一个map
,来存储字符串中出现的每个元素对应的次数,然后对map
的值进行排序,获得前k
个元素的键,前k
个键就是字符串中前k
个高频元素。
代码实现:
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
map<int, int> countMap;
for(auto &num : nums) countMap[num]++;
vector<pair<int, int>> freq(countMap.begin(), countMap.end());
sort(freq.begin(), freq.end(), [](const pair<int, int> &a, const pair<int, int> &b){ return a.second > b.second; });
vector<int> res;
for(int i = 0; i < k; ++i) {
res.push_back(freq[i].first);
}
return res;
}
};
优先级队列的思想:
通过小顶堆记录前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; // map<nums[i],对应出现的次数>
for (int i = 0; i < nums.size(); i++) {
map[nums[i]]++;
}
// 对频率排序
// 定义一个小顶堆,大小为k
priority_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,则队列弹出,保证堆的大小一直为k
pri_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;
}
};