目录
1、有效括号
有效的括号https://leetcode.cn/problems/valid-parentheses/
(1)题目描述
(2)解题思路
1.定义一个字符栈,遍历字符串s
2.如果当前字符是 '(' 或者 '[' 或者 '{',直接入栈
如果当前字符是')' 或者 ']' 或者 '}',将栈顶元素取出(取出的栈顶元素必然是左括号),并出栈,将取出的栈顶元素与当前字符做比较(当前字符必然是右括号),判定是否匹配,如果不匹配直接返回false。匹配,继续执行当前逻辑。
当栈为空时,说明字符串的括号全部都顺利闭合,返回true,否则返回false
class Solution
{
public:
bool isValid(string s)
{
stack<char> st; // 定义一个字符栈
for(auto ch : s) // 遍历字符
{
if(ch == '(' || ch == '[' || ch == '{') // 左括号入栈
{
st.push(ch);
}
else // 右括号,取出栈顶元素比较
{
if(st.empty()) //如果栈为空,则该右括号必然没有与之匹配的左括号,直接返回false
return false;
char top = st.top();
st.pop();
if(ch == ')' && top != '(' ||
ch == ']' && top != '[' ||
ch == '}' && top != '{')
{
return false;
}
}
}
return st.empty();
}
};
2、删除字符串中的重复项
1047. 删除字符串中的所有相邻重复项https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string/
(1)题目描述
(2)解题思路
1.定义一个栈,遍历字符串
此处可以使用string模拟栈
2.如果当前栈不为空并且栈顶元素与当前字符串相同,直接将栈顶元素出出去
否则将当前元素入栈
3.直接将string模拟的栈返回
举个栗子: s = "abbaca"
遍历到第一个字符a的时候,此时栈为空,就将a入栈
s = "abbaca"
遍历到第二个字符b的时候,栈不为空,当前字符b和栈顶元素a相同吗?不同,入栈
s = "abbaca"
遍历到第三个字符b的时候,栈不为空,当前字符b和栈顶元素b相同吗?相同,将栈顶元素b出栈
s = "abbaca"
遍历到第四个字符a的时候,栈不为空,当前字符a和栈顶元素a相同吗?相同,将栈顶元素a出栈
s = "abbaca"
遍历到第五个字符c的时候,栈为空,直接入栈
s = "abbaca"
遍历到第六个字符a的时候,栈不为空,当前字符a和栈顶元素c相同吗?不同,入栈
此时栈的元素就是结果。
这里可以对照着代码看,是非常清晰的。
class Solution
{
public:
string removeDuplicates(string s)
{
string stack;
for(auto e : s)
{
if(!stack.empty() && e == stack.back()) stack.pop_back();
else stack.push_back(e);
}
return stack;
}
};
3、比较含退格的字符串
844. 比较含退格的字符串https://leetcode.cn/problems/backspace-string-compare/
(1)题目描述
(2)解题思路
这道题和第二题是极其相似的,可以说是几乎一模一样
1.定义两个栈,这里可以用string模,一般这种与字符串相关的栈题目,都可以用string来充当栈
2.遍历s字符串
如果栈不为空,并且当前字符是 '#',直接出栈顶元素
如果当前元素不是 '#',直接入栈
3.遍历t字符串
如果栈不为空,并且当前字符是 '#',直接出栈顶元素
如果当前元素不是 '#',直接入栈
4.直接返回两个字符串的比较结果
class Solution
{
public:
bool backspaceCompare(string s, string t)
{
string s1, t1;
for(auto e : s)
{
if(s1.size() && e == '#') s1.pop_back();
else if(e != '#'){
s1.push_back(e);
}
}
for(auto e : t)
{
if(t1.size() && e == '#') t1.pop_back();
else if(e != '#'){
t1.push_back(e);
}
}
return s1 == t1;
}
};
4、基本计算器II
227. 基本计算器 IIhttps://leetcode.cn/problems/basic-calculator-ii/
(1)题目描述
(2)解题思路
这道题是表达式求值,关于这种表达式求值的题,解法都是通用的,就是用两个栈模拟。这道题可以只用一个栈,数值栈,和一个存放运算符的字符变量,来模拟。
这道题只有+-*/四个运算符,因此,优先级最高的当然是 * 和 /。
我们需要用到一个存放整数的栈和一个运算符变量 op
这个栈刚开始是空的,但是这个 op,刚开始是多少呢?刚开始可以为 '+',表示在这个加上这个字符串,比如给的字符串是 s = "3+9-150*3/9",就表示 + 3+9-150*3/9
我们就用s="3+9-150*3/9"这个字符串来模拟一下
s="3+9-150*3/9"
1.遍历字符串s
s="3+9-150*3/9"
2.第一个字符是一个整数3,提取出来,判断一下op,是+,因此将3入栈
s="3+9-150*3/9"
3.第二个字符是符号+,将op更新为+
s="3+9-150*3/9"
4.第三个字符是一个整数9,提取出来,判定一下op,是+,因此将9入栈
此时你能不能计算3+9的结果呢?是不能的,因为你并不知道下一个运算符是什么,如果是*/,是要先计算9和后面的那一个数的结果的。
s="3+9-150*3/9"
5.第四个字符是符号-,将op更新为-
s="3+9-150*3/9"
6.第5、6、7个字符都是整数,是一个三位数150,要把这个整数提取出来,是很好提取的,写一个循环,定义一个tmp,每次 *= 10,在把当前字符转整数加给tmp,判定一下op,op是负号,将-150入栈
s="3+9-150*3/9"
7.第8个运算符是* ,将更新op为*
s="3+9-150*3/9"
8.第9个字符是数字3,提取出来,判定一下op,op是*,优先级是最高的,此时可以直接计算,将栈顶的-150取出来并出栈,与3做*运算,在将结果-450入栈
s="3+9-150*3/9"
9. 第10个字符是运算符/,更新op为/
s="3+9-150*3/9"
10.第11个字符是数字9,提取出来,在看看op为/,优先级最高,可直接运算,将栈顶元素-450取出来并出栈与9做除法运算,将结果-50入栈
11.遍历完毕之后,此时直接将栈的元素做加法运算,就可得出结果-38
总结:这道题其实就是一个分类讨论的过程,当然,这里题目给的字符串是有空格的,如果有空格跳过即可
class Solution
{
public:
int calculate(string s)
{
char op = '+';
stack<int> st;
int i = 0;
while(i < s.size())
{
//空格直接跳过
if(s[i] == ' ') i++;
else
{
//如果字符是运算符,更新运算符
if(s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/') op = s[i++];
//是数字
else
{
// 1.提取数字
int tmp = 0;
while(s[i] >= '0' && s[i] <= '9')
{
tmp *= 10;
tmp += s[i] - '0';
i++;
}
// 2.判定运算符
if(op == '+') st.push(tmp);
else if(op == '-') st.push(-tmp);
else if(op == '*')
{
int top = st.top();
st.pop();
st.push(top * tmp);
}
else // '/'
{
int top = st.top();
st.pop();
st.push(top / tmp );
}
}
}
}
int ret = 0;
while(!st.empty())
{
ret += st.top();
st.pop();
}
return ret;
}
};
5、字符串解码
(1)题目描述
394. 字符串解码https://leetcode.cn/problems/decode-string/
(2)解题思路
这道题需要用栈模拟。定义两个栈,一个数字栈,一个字符栈。
我们以s = "1[a2[bc]]de3[f]" 字符串为例
1、遍历字符串,碰到第一个字符'1',入数字栈。
2、遍历到'['将'['后面的字符a提取出来,入栈
3、此时遇到到字符'2',入数字栈
4、此时遇到'[' ,将后面的字符'bc'提取出来,入字符栈
5、此时遇到']',数字栈和字符栈出栈, 将2 个 bc,也就是bcbc,可直接bcbc入到字符栈顶吗?不可以呀,因为不知道后面是否还有 ']',因此要将它加入栈顶元素后面,也就是'a'的后面
6、此时又遇到']',将数字栈和字符栈出栈,将1个abcbc,加入栈顶元素后面,可此时将abcbc出栈后,栈为空,访问栈顶不就报错了?因此我们要提前将一个空字符串入栈'',此时将abcbc加入到空字符后面还是abcbc
7、遇到普通字符,将'de'加入字符栈顶元素后面。
8、遇到数字3,入数字栈
9、遇到'[',将后面的字符f提取出来入字符栈
10、遇到']',将数字栈和字符出栈,将fff加入栈顶元素后面,也就是abcdcdefff
11、遍历结束后,此时字符栈的栈顶元素就是结果
class Solution
{
public:
string decodeString(string s)
{
stack<int> numst; //数字栈
stack<string> strst; //字符栈
strst.push(""); //入一个空字符串
int i = 0, n = s.size();
while(i < n)
{
// 遇到数字,提取出数字,入数字栈
if(s[i] >= '0' && s[i] <= '9')
{
int tmp = 0;
while(i < n && s[i] >= '0' && s[i] <= '9')
{
tmp *= 10;
tmp += s[i++] - '0';
}
numst.push(tmp);
}
else
{
// '['
if(s[i] == '[')
{
i++;
string tmp;
while(i < n && s[i] >= 'a' && s[i] <= 'z')
tmp += s[i++];
strst.push(tmp);
}
// ']
else if(s[i] == ']')
{
string stop = strst.top();
strst.pop();
int count = numst.top();
numst.pop();
string newstr;
while(count--)
{
newstr += stop;
}
string newtop = strst.top();
strst.pop();
strst.push(newtop + newstr);
i++;
}
else // 普通字符
{
string tmp;
while(i < n && s[i] >= 'a' && s[i] <= 'z')
tmp += s[i++];
string top = strst.top();
strst.pop();
strst.push(top + tmp);
}
}
}
return strst.top();
}
};
6、验证栈序列
946. 验证栈序列https://leetcode.cn/problems/validate-stack-sequences/
(1)题目描述
(2)解题思路
这道题,我们在入栈的时候,同时出栈即可。
比如pushed = [1, 2, 3, 4, 5] poped = [4, 5,3,2,1],定义一个poped的下标i = 0, 遍历pushed,将1入栈,然后判定poped[i]也就是4是否等于当前栈顶的元素,如果等于出栈,然后下标i++,进行循环判定
class Solution
{
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped)
{
vector<int> st;
int i = 0;
for(auto e : pushed)
{
// 入栈的同时出栈
st.push_back(e);
while(!st.empty() && popped[i] == st.back())
{
st.pop_back();
i++;
}
}
return st.empty();
}
};
7、最小栈
155. 最小栈https://leetcode.cn/problems/min-stack/
(1)题目描述
(2)解题思路
这道题最主要的是我们如何获取栈中的最小元素呢?遍历吗?每次遍历一下,取出最小的元素,可以是可以但是效率太低了,每一次取都要去遍历,有没有更好的方法呢?我们可以用两个栈,一个栈存放数据,一个栈存放栈中的最小元素。比如说7,5,2,2,5
7的时候入栈,此时最小栈为空,也要入7,然后到5,5入栈,5比最小栈的栈顶7小,入栈。2入数据栈,2比最小栈栈顶元素小,入栈,下一个2,这个2要不要入最小栈呢?是要的,否则出栈时会问题的。
此时我们取栈顶最小元素,是5,但栈的最小元素应该是2呀。因此,如果这个元素和最小栈的栈顶元素<=的时候,都要入栈
class MinStack {
public:
MinStack() {
// 不需要初始化
}
void push(int val) {
st.push(val);
if(min_st.empty() || val <= min_st.top())
{
min_st.push(val);
}
}
void pop()
{
if(st.top() == min_st.top())
{
min_st.pop();
}
st.pop();
}
int top() {
return st.top();
}
int getMin() {
return min_st.top();
}
stack<int> st;
stack<int> min_st;
};
8、逆波兰表达式求值
150. 逆波兰表达式求值https://leetcode.cn/problems/evaluate-reverse-polish-notation/
(1)题目描述
(2)解题思路
我们平常写的表达式都是中缀表达式,比如说2 * 4 + 1,中缀表达式计算是很不友好的,因为涉及到优先级,我们人可以直接自己判断优先级,但是计算机就没那么容易了,因此就有了后缀表达式,比如说示例1: 2,1,+,3,*,后缀表达式如何计算呢,遇到符号直接运算,2 + 1 = 3,然后再遇到乘号,用2 + 1的结果3,在乘以3。
用栈是很好模拟的,比如说示例2的 2,1,+,3,*
遇到操作数直接入栈, 2,1入栈
遇到+,取出栈顶两元素,计算结果入栈
遇到3,入栈
遇到*,取出栈顶2元素,计算结果入栈,注意,这里先出来的是右操作数,在计算除法的时候不要左右操作数写反了。
最后栈顶元素即为表达式结果
class Solution {
public:
int evalRPN(vector<string>& tokens)
{
stack<int> st;
for(auto& e : tokens)
{
if(e == "+" || e == "-" || e == "*" || e == "/")
{
int right = st.top(); st.pop();
int left = st.top(); st.pop();
if(e == "+")
st.push(left + right);
else if(e == "-")
st.push(left - right);
else if(e == "*")
st.push(left * right);
else
st.push(left / right);
}
else
{
st.push(stoi(e));
}
}
return st.top();
}
};