博客主页: A_SHOWY
系列专栏:力扣刷题总结录 数据结构 云计算 数字图像处理 力扣每日一题_
队列是先进先出,栈是先进后出。同时二者都是容器适配器而不是容器。
232.用栈实现队列 | easy | 基础操作 |
225.用队列实现栈 | easy | 基础操作 |
20.有效的括号 | easy | 碰到左括号存栈里,等右括号匹配 |
1047.删除字符串中所有的重复项 | mid | 匹配相邻元素消除 |
150.逆波兰式求和 | mid | 字符转换整型 |
239.滑动窗口的最大值 | hard | 双值队列的应用,巧妙 |
232. 用栈实现队列https://leetcode.cn/problems/implement-queue-using-stacks/我们知道栈是先进先出,队列是先进后出。所以要用栈来实现队列,需要一个输入栈一个输出栈(按输入的顺序倒叙存放)。这个题目有四个操作
1.第一个操作很容易就是一个入队列的操作和入栈一样
void push(int x) {
stkIn.push(x);
}
2.第二个操作是出队列的操作,这个操作就有一定的难度,我们需要一个 输入栈和一个输出栈,当输出栈为空的时候,我们需要把输入栈的所有元素全部弹出到输出栈,这样从输出栈进行pop这个顺序满足先入先出。输出栈弹出的第一个元素就我们的结果。
int pop() {
//当out栈为空的时候
if(stkOut.empty()){
while(!stkIn.empty()){//把所有in栈里面的东西全部弹出给out,否则顺序不对
stkOut.push(stkIn.top());
stkIn.pop();
}
}
int res = stkOut.top();
stkOut.pop();
return res;
3.第三个操作是返回队列开头的元素,我们发现和第二个操作是大量重复代码相似的操作,所以我们可以直接复制第二问的代码,但是我们专业来说可以用this->来直接调用同一个类中的函数,调用结束后发现多弹出一个元素,我们再push回去就可以了。
int peek() {
//学会运用this->,这里发现和pop操作的代码大同小异
int res = this -> pop();
//发现这里多弹出了
stkOut.push(res);
return res;
}
第四个操作是 判断队列是否为空,直接判断入栈和出栈相与为空,说明两个栈都没有元素时为空既可。
bool empty() {
return stkIn.empty() && stkOut.empty();
}
225. 用队列实现栈https://leetcode.cn/problems/implement-stack-using-queues/这道题目其实就是用一个队列来实现栈,具体思路和上一题有很多相似之处。
1.首先第一个问题还是一个push操作,栈和队列的push操作是一样的,所以可以直接push
void push(int x) {
q.push(x);
}
2.第二个pop操作还是整个题目最需要思考的。当一个队列的时候,我们的整体思路是把size-1个元素取出来然后重新push进入队列。再pop队列front的元素一次。
int pop() {
int size = q.size();
size--;
while(size--){
q.push(q.front());
q.pop();
}
int res = q.front();
q.pop();
return res;
}
3.第三个操作直接使用back就可以解决
int top() {
return q.back();
}
4.第四个操作,空值操作
bool empty() {
return q.empty();
}
可以看出来整体思路还是比较简单的,除了pop操作需要多思考一下,其他小问的要求容易。
拓展:如果要求使用两个队列来满足栈的pop操作
将第一个队列的size-1个元素移动到第二个队列中 ,留下的最后一个元素就是要返回的值,再把第二个队列中的元素移动到第一个队列中即可。核心思想不变。但是记住要把q1最后清空。
int pop() {
// int size = q.size();
// size--;
// while(size--){
// q.push(q.front());
// q.pop();
// }
// int res = q.front();
// q.pop();
// return res;
int size = q.size();
size--;
while(size--){
q1.push(q.front());
q.pop();
}
int res = q.front();
q.pop();
q = q1;
//还要清空q1
while(!q1.empty())
{
q1.pop();
}
return res;
}
20. 有效的括号https://leetcode.cn/problems/valid-parentheses/栈非常适合做这种匹配的问题,有一个小技巧,就是碰到左括号,就存一个右括号到栈里,再进行匹配。
class Solution {
public:
bool isValid(string s) {
stack st;
for(int i = 0; i< s.size();i++){
if(s[i] == '(') st.push(')');
else if(s[i] == '{') st.push('}');
else if(s[i] == '[') st.push(']');
//第二种
else if(st.empty() || s[i] != st.top()) return false;
else st.pop();
}
return (st.empty());
}
};
1047. 删除字符串中的所有相邻重复项https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string/ 本题要删除相邻相同元素,相对于20. 有效的括号 (opens new window)来说其实也是匹配问题,20. 有效的括号是匹配左右括号,本题是匹配相邻元素,最后都是做消除的操作。用栈来存放,那么栈的目的,就是存放遍历过的元素,当遍历当前的这个元素的时候,去栈里看一下我们是不是遍历过相同数值的相邻元素。这种消除问题也是栈的经典题目。
class Solution {
public:
string removeDuplicates(string s) {
stack st;
for(char x : s){
if(st.empty() || x != st.top()) st.push(x);//如果为空或者不相等直接push
else st.pop();//否则pop
}
//输出操作,栈先进后出,所以最好输出要反过来
string result = "";
while(!st.empty()){
result += st.top();
st.pop();
}
reverse(result.begin(),result.end());
return result;
}
};
150. 逆波兰表达式求值https://leetcode.cn/problems/evaluate-reverse-polish-notation/
本题也是一道和栈有关的题目,要求求逆波兰式的求值,对于这道题目,我们可以考虑到当在一个栈中栈顶为符号的时候,就从栈顶拿出来两个元素进行符号操作再放回栈顶。
需要注意的是这道题给的范围定义的long long的栈,最后字符串转换的时候要用long long的stoll
注意本题给的是tokens数组中全部都是字符串,所以在push的时候需要进行转化
stoi 函数:将字符串转成 int 整数。 stol 函数:将字符串转成 long 整数。 stoll 函数:将字符串转成 long long 整数。
class Solution {
public:
int evalRPN(vector& tokens) {
stack st;
for(int i = 0 ; i < tokens.size(); i++){
if(tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/") {
int nums1 = st.top();
st.pop();
int nums2 = st.top();
st.pop();
if(tokens[i] == "+") st.push(nums2 + nums1);
if(tokens[i] == "-") st.push(nums2 - nums1);
if(tokens[i] == "*") st.push(nums2 * nums1);
if(tokens[i] == "/") st.push(nums2 / nums1);
}
else st.push(stoi(tokens[i]));
}
return st.top();
}
};
239. 滑动窗口最大值https://leetcode.cn/problems/sliding-window-maximum/这道题目是一个hard题目,思路确实很难,对于本题,我们需要一个队列,这个队列呢,放进去窗口里的元素,然后随着窗口的移动,队列也一进一出,每次移动之后,队列告诉我们里面的最大值是什么。
队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。那么这个维护元素单调递减的队列就叫做单调队列,即单调递减或单调递增的队列。
我们用到的自定义的队列是一个deque,双向队列,也就是左右都能进出。我们先考虑push函数,在我们每次push的时候,当要push的元素比前面的都大时,把前面的元素全部删除,因为我们不需要维护所有的元素,我们只需要保证最大元素即可,可能有人就迷惑了,你把前面的元素都卷走了,那pop操作怎么办。pop操作就更为巧妙了,当要pop的元素和队列的front相等的时候,才是真正要pop 的时候。但是对于push和pop函数队列操作不要忘记队列不为空的判断,front函数就简单了,直接返回队列的front就可以。
在实现函数部分,先把前k个元素push进队列,找到最大的值,然后从第k+1个元素进行push,pop,front操作。最后放到数组里面返回。
class Solution {
private:
class MyQueue{
public:
deque que;
void pop(int value){
if(!que.empty() && que.front() == value){
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 maxSlidingWindow(vector& nums, int k) {
MyQueue que;
vector res;
for(int i = 0 ; i < k; i++){
que.push(nums[i]);//先把前k个push进去
}
res.push_back(que.front());//记录前k个元素的最大值
for(int i = k; i < nums.size();i++){
que.pop(nums[i-k]);
que.push(nums[i]);
res.push_back(que.front());
}
return res;
}
};
这道题目的重点是我们要确定维护的是大的元素,前面的元素较小在push操作时候就会被顶出。
本期总结了道题目,后续如果有相关题目会继续总结添加。