一、栈介绍
1、定义
栈也是一种数据呈线性排列的数据结构,不过在这种结构中,我们只能访问最新添加的数据。从栈顶放入元素的操作叫入栈,取出元素叫出栈。
2、优缺点及使用场景
优点:高效的操作、简单易用、空间效率高等
缺点:局限性、容量限制、内存管理、不支持随机访问等。
使用场景:递归算法、括号匹配、表达式求值等。
3、基本操作
Push--在顶部插入一个元素
Pop--返回并移除栈顶元素
isEmpty--如果栈为空,则返回true
Top--返回顶部元素,但并不移除它
二、常考算法
1、使用栈计算后缀表达式
题目:根据逆波兰表示法,求表达式的值。
示例:输入: ["10", "6", "9", "3", "+", "-11", " * ", "/", " * ", "17", "+", "5", "+"],输出: 22
逆波兰表达式主要有以下两个优点:
-
去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
-
适合用栈操作运算:遇到数字则入栈;遇到运算符则取出栈顶两个数字进行计算,并将结果压入栈中。
#include<stack>
#include<vector>
#include<string>
#include<iostream>
using namespace std;
int evaluate_postfix(vector<string> v){
stack<long long> st;
for(int i = 0; i < v.size(); i++){
if (v[i] == "+" || v[i] == "-" || v[i] == "*" || v[i] == "/"){
long long num1 = st.top();
st.pop();
long long num2 = st.top();
st.pop();
if(v[i] == "+") st.push(num2 + num1);
if(v[i] == "-") st.push(num2 - num1);
if(v[i] == "*") st.push(num2 * num1);
if(v[i] == "/") st.push(num2 / num1);
}
else{
st.push(stoll(v[i]));
}
}
int result = st.top();
st.pop();
return result;
}
int main(){
vector<string> v = {"10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"};
int res;
res = evaluate_postfix(v);
cout << res;
}
2、对栈的元素进行排序
题目:input:[3, 1, 4, 2, 5] output:[5, 4, 3, 2, 1]
思路:从原始栈中取出元素,将其插入到结果栈中的正确位置,以实现排序。在Python中,使用了while
循环和pop
操作,而在C++中使用了while
循环和top
、pop
操作。最终,返回的结果栈中包含了排序好的元素。
#include<stack>
#include<iostream>
using namespace std;
stack<int> sort_stack(stack<int> input_stack){
stack<int> result_stack;
while(!input_stack.empty()){
int temp = input_stack.top();
input_stack.pop();
while(!result_stack.empty() && result_stack.top() < temp){
input_stack.push(result_stack.top());
result_stack.pop();
}
result_stack.push(temp);
}
return result_stack;
}
int main() {
stack<int> input_stack;
input_stack.push(3);
input_stack.push(1);
input_stack.push(4);
input_stack.push(2);
input_stack.push(5);
stack<int> sorted_stack = sort_stack(input_stack);
while (!sorted_stack.empty()) {
cout << sorted_stack.top() << " ";
sorted_stack.pop();
}
return 0;
}
3、判断表达式是否括号平衡
题目:给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
示例:input:"([{}]()",output:False;
思路:主要分为以下三种情况:
(1)已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false。
(2)遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false。
(3)遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false。
技巧:在匹配左括号的时候,右括号先入栈,就只需要比较当前元素和栈顶相不相等就可以了。
#include<stack>
#include<string>
#include<iostream>
using namespace std;
bool isvalid(string s){
if (s.size() % 2 == 1) // 如果s的长度为奇数,一定不符合要求
return false;
stack<char> 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() || st.top() != s[i])
// 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号 return false
// 第二种情况:遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符。所以return false
return false;
else st.pop(); // st.top() 与 s[i]相等,栈弹出元素
}
// 第一种情况:此时我们已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false,否则就return true
return st.empty();
}
// 当bool类型的值为0时,它表示false,而当bool类型的值非零时,它表示true
int main(){
bool s,s1,s2,s3;
s = isvalid("([{}]()");
cout << s << endl;
s1 = isvalid("([{}}}");
cout << s1<< endl;
s2 = isvalid("([{}])))");
cout << s2<< endl;
s3 = isvalid("{[]}");
cout << s3;
}
- 时间复杂度: O(n)
- 空间复杂度: O(n)