逆波兰表达式又叫做后缀表达式,即将运算符写在 *** 作数之后,是一种没有括号、严格遵循从左到右运算的后缀式表达方法。
表达式E的后缀形式:
如果E是E1 op E2形式的表达式,这里op是任何二元 *** 作符,则E的后缀式为E1’E2’ op,这里E1’和E2’分别为E1和E2的后缀式。
如:(1 + 2) * (3 + 4)
写成后缀形式为1 2 + 3 4 + *
逆波兰表达式适合用栈 *** 作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算(先出栈的是右 *** 作数,后出栈的是左 *** 作数),并将结果压入栈中。
class Solution {
public:
int evalRPN(veclass="superseo">ctor<string>& tokens) {
stack<int> st;
for (int i = 0; i < tokens.size(); i++) {
if (tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/") {
int rightnum = st.top();
st.pop();
int leftnum = st.top();
st.pop();
if (tokens[i] == "+") st.push(leftnum + rightnum);
if (tokens[i] == "-") st.push(leftnum - rightnum);
if (tokens[i] == "*") st.push(leftnum * rightnum);
if (tokens[i] == "/") st.push(leftnum / rightnum);
}
else {
st.push(stoi(tokens[i])); //stoi:数字字符串转换成int输出
}
}
return st.top();
}
};
题目2:239.滑动窗口最大值
解法一:优先队列
C++ STL priority_queue容器适配器详解
优先队列priority_queue遵循"first in,largest out"原则,先进队列的元素并不一定先出队列,而是优先级最大的元素先出队列,默认按照元素从大到小进行排序。
优先级队列可以理解为披着队列外衣的堆,堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。
采用最大堆的数据结构来保存元素,堆顶元素即为当前堆的最大值,并判断当前堆顶元素这是否在窗口中,在则直接返回,不在则删除堆顶元素并调整堆。
窗口遍历数组
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> result;
priority_queue<pair<int, int>> pq;
for (int i = 0; i < k; i++) { //将数组的前k个元素放入优先队列中
pq.push(make_pair(nums[i], i)); //或pq.emplace(nums[i], i);
}
result.push_back(pq.top().first); //堆顶就是最大元素
for (int i = k; i < nums.size(); i++) { //滑动窗口从索引为k的元素开始遍历,将新进入滑动窗口的元素加堆中
pq.push(make_pair(nums[i], i));
while (pq.top().second <= i - k) { //当堆顶元素不在滑动窗口中的时候,不断删除堆顶堆元素,直到最大值在滑动窗口里
pq.pop();
}
result.push_back(pq.top().first);
}
return result;
}
};
解法二:单调队列(双端队列)——最优解
单调队列即队列中所有的元素都是单调的,要么单增要么单减;
新增元素时添加到队尾,若队列不符合单调性就将队尾元素出队,直到队列单调或空。
之后的遍历同理。
注意,队列中保存的并不是数,而是数组中该数的下标。
还要考虑当队首元素不在滑动窗口时的情况:
L、R来表示窗口的左边界和右边界,R可以用下标i
来表示,那么左边界L为i-k+1
,当队首元素的下标小于滑动窗口左边界L时表示队首元素已经不再滑动窗口内
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int> q; //双端队列
for (int i = 0; i < k; i++) { //将数组的前k个元素放入单调队列中
while (!q.empty() && nums[i] >= nums[q.back()]) { //如果遍历到的元素大于等于单调队列队尾元素,则不满足从大到小
q.pop_back(); //那么就将队尾元素移除
}
q.push_back(i); //如果队列为空则跳过while循环并将遍历到的元素添加到队尾
}
vector<int> result = { nums[q.front()] }; //窗口大小为k,将队首的元素放进结果数组里
for (int i = k; i < nums.size(); i++) { //滑动窗口从索引为k的元素开始遍历
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
while (q.front() <= i - k) { //判断队首的元素是否在滑动窗口范围内 等同于q.front() <= i - k + 1
q.pop_front();
}
result.push_back(nums[q.front()]);
}
return result;
}
};
评论区大佬更巧妙的代码:
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int>q; //双端队列
vector<int>res;
for (int i = 0; i < nums.size(); i++) {
while (q.size() && i - k + 1 > q.front()) q.pop_front(); //判断队头是否在滑动窗口范围内
while (q.size() && nums[i] >= nums[q.back()]) q.pop_back();//维护单调递减队列
q.push_back(i); //将当前元素插入队尾
if (i >= k - 1) res.push_back(nums[q.front()]); //滑动窗口的元素达到了k个,才可以将其加入答案数组中
}
return res;
}
};
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)