栈本身不是一个很难的数据结构, 但是对应的算法题有一定难度, 难在我们是否能想到这道题用栈解决. 如果知道用栈解决这道题, 那其实本质就是模拟.
题目1: 删除字符中的所有相邻重复项
cpp
class Solution {
public:
string removeDuplicates(string s) {
stack<char> st;
string ret;
for(const auto& e: s)
{
if(st.empty())
st.push(e);
else if(e == st.top())
st.pop();
else
st.push(e);
}
while(!st.empty())
{
ret += st.top();
st.pop();
}
reverse(ret.begin(),ret.end());
return ret;
}
};
用栈最后把元素拷贝到字符串中, 最后还需要逆序一次, 可以直接用字符串模拟栈的行为即可:
cpp
class Solution {
public:
string removeDuplicates(string s) {
string ret;
for(const auto& e: s)
{
if(ret.length() == 0)
ret += e;
else if(ret.back() == e)
ret.pop_back();
else
ret += e;
}
return ret;
}
};
题目1.2 比较含退格的字符串
这题和题目1几乎一样, 用栈模拟即可.
cpp
class Solution {
public:
bool backspaceCompare(string s, string t) {
string tmpS, tmpT;
for(const auto& e : s)
{
if(!tmpS.empty() && e == '#')
//e为#,栈不为空,就退格
tmpS.pop_back();
else if(e == '#')
//如果e为#, tmpS为空, 就不用退格了
continue;
else
tmpS += e;
}
//对T进行同样的操作
for(const auto& e : t)
{
if(!tmpT.empty() && e == '#')
tmpT.pop_back();
else if(e == '#')
continue;
else
tmpT += e;
}
return tmpS == tmpT;
}
};
题目2:基本计算器 II
此题有两种思路, 比较麻烦的一种是中缀转后缀表达式, 然后后缀表达式进行计算, 基本计算器I 可以用这种思路解.
由于此题没有括号的运算, 只有加减乘除, 可以直接从左到右依次遍历, 将值 或者 **通过乘除法计算后的值 ,**放入栈中, 然后依次出栈将结果累加即可, 具体分为:
-
符号是+/-, 该数是否被立即计算是不确定的, 先入栈等待, 如果是减则入栈一个负值, 因为最后的计算全是加法. 如果下一个符号仍是+-, 则前一个数就被保留至最终运算了, 比如2+3
-
符号是*/, 这是此题优先级最高的运算, 遇到它需要把栈顶正在"等待"的元素取出进行乘除法运算, 再放回栈里. 比如 2+ 3 * 4, 遇到4时取出栈顶的3, 计算为12压入栈中.
cpp
class Solution {
public:
int calculate(string s) {
stack<int> st;
char Op = '+';
int left = 0, right = 0;
int n = s.size();
while(right < n)
{
//跳过空白
if(s[right] == ' ')
++right;
//数字
else if(isdigit(s[right]))
{
//先把数字提取出来, 可能有多位
int num = 0;
while(right < n && isdigit(s[right]))
num = num*10 + (s[right++]-'0');
if(Op == '+') st.push(num);
else if(Op == '-') st.push(-num);
else if(Op == '*')
{
num = st.top() * num;
st.pop();
st.push(num);
}
else
{
num = st.top() / num;
st.pop();
st.push(num);
}
}
else
Op = s[right++];
}
int ret = 0;
while(!st.empty())
{
ret += st.top();
st.pop();
}
return ret;
}
};
题目3: 字符串解码
此题思路要用到双栈, 由于我们需要从内向外解码字符串, 每次都要先解码内部[]的字符串, 层层向外.
从左到右扫描, 将扫描的内容分为四种情况:
-
数字num, 则压入数字栈中, 等待 ']', 作为字符串重复的次数
-
'[', 将后面紧跟的(也可能没有)字符串压入字符串栈中, 等待']', 作为字符串重复的内容
-
']', 取出两个栈栈顶的元素, 开始重复. 将结果与**新栈顶的字符串合并,**因为内层解码的目的是把内容提供给外层作为外层字符串的一部分, 所以需要合并.
-
字符, 这时说明遇到了一个"裸"的字符串, 即只单纯重复一次, 同上面一样, 与栈顶字符串合并.
以一个复杂的例子为例: 3[z] 2[2[y]pq4[a]] ef
cpp
class Solution {
public:
string decodeString(string s) {
stack<int> stNum;
stack<string> stStr;
stStr.push("");
int i = 0, n = s.size();
while (i < n)
{
if (isdigit(s[i]))
{
int tmpNum = 0;
while (i < n && s[i] >= '0' && s[i] <= '9')
tmpNum = tmpNum * 10 + (s[i++] - '0');
stNum.push(tmpNum);
}
else if (s[i] == '[')
{
++i;
string tmpStr;
while (isalpha(s[i]))
tmpStr += s[i++];
stStr.push(tmpStr);
}
else if (s[i] == ']')
{
int num = stNum.top();
stNum.pop();
string str = stStr.top();
stStr.pop();
while (num--)
stStr.top() += str;
++i;
}
else
{
while (i < n && isalpha(s[i]))
stStr.top() += s[i++];
}
}
return stStr.top();
}
};
题目4: 验证栈序列
此题和数据结构做过的选择题很像, 不过验证是否能按照指定顺序出栈 .
思路都是一样的, 依次将pushed的元素入栈, 每入一个元素就检查一次poped栈是否能出栈, 而且要循环检查, 因为可能出了一个元素就能出一连串的元素. 最后所有元素都出栈则验证成功.
cpp
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
stack<int> st;
int j = 0;
for(const auto & e : pushed)
{
st.push(e);
while(!st.empty() && st.top() == popped[j])
{
st.pop();
++j;
}
}
return st.empty();
}
};
暂完