栈特点:先进后出
STACK的六种基本操作
①bool empty();
如果当前堆栈为空,empty() 函数 返回 true 否则返回false.
②void pop();
pop() 函数移除堆栈中最顶层元素。
③void push( const TYPE &val );
push() 函数将 val 值压栈,使其成为栈顶的第一个元素。
④size_type size();
size() 函数返当前堆栈中的元素数目。
⑤TYPE &top();
top() 函数返回对栈顶元素的引用。
⑥“==” “<=” “>=” “<” “>” "!="
所有的这些操作可以被用于栈. 相等指栈有相同的元素并有着相同的顺序。
括号配对问题
算法思想:
- 顺序扫描字符串,遇到左括号时候让该括号进栈;
- 扫描到右括号时,比较该括号是否与栈顶元素匹配,若匹配则用pop()将栈顶元素移出,继续判断;若不匹配,则配对次序不正确,匹配失败,直接退出;
- 扫描到右括号而堆栈已为空,则右括号多于左括号,匹配失败,直接退出;
- 字符串扫描结束时,若栈非空(即栈中还存在未匹配的左括号),说明左括号多于右括号,匹配失败;若栈为空,则匹配成功。
例1:对输入的一段字符串进行括号匹配检查,若语句中左括号和右括号数目相等,且能够完整的匹配成对,即不存在括号匹配上的错误,输出“OK”字样,否则输出“Wrong”字样。
样例输入:
)( ((()) () (a)((b)((c)(d)))
样例输出:
Wrong Wrong OK OK
#include<bits/stdc++.h> #define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; int main() { fio stack<char> small; char s[10001]; cin >> s; for (int i = 0; i < strlen(s); i++) { switch(s[i]) { case '(': small.push(s[i]);break; case ')': if (!small.empty()) { small.pop(); break; } if (small.empty()) {//栈中没有'('与之匹配 cout << "Wrong"<< endl; return 0; } } } if (small.empty()) { cout << "OK" << endl; } else {//栈中还存在未配对的'(' cout << "Wrong" << endl; } }
例2:现在,有一行括号序列,请你检查这行括号是否配对。
输入:每组输入数据都是一个字符串S(S的长度小于10000,且S不是空串),数据保证S中只含有"[","]","(",")","{","}"六种字符
输出:每组输入数据的输出占一行,如果该字符串中所含的括号是配对的,则输出“OK”,如果不配对则输出“Wrong”
样例输入
{}[(]} {[()]} ){}(
样例输出
Wrong OK Wrong
#include<bits/stdc++.h> #define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; bool judge(char *p) { stack<char> s; while (*p) { if ((*p == '{') || (*p == '[') || (*p == '(')) { s.push(*p); p++; } else { if (s.empty())//若右括号多于左括号将会产生错误,p将指向非法区域,*p错误 return false; if (s.top() == '{' && *p == '}' || s.top() == '[' && *p == ']'|| s.top() == '(' && *p == ')') { s.pop(); p++; } else { return false; } } } if (!s.empty()) return false; else return true; } int main() { fio char s[10001]; cin >> s; if (judge(s)) cout << "OK" << endl; else cout << "Wrong" << endl; }
表达式转换和求值问题
1、逆波兰表达式简介
核心思想:中缀表达式转换为后缀表达式。
中缀表达式:(9+6)* 7 - 8/2=====>>>>>>后缀表达式:9 6 + 7* 8 2 / -
优点:
- 去除原来表达式中的括号,因为括号只指示运算顺序,不是完成计算必须的元素。
- 后缀表达式不利于人阅读,但利于计算机处理。
2、将中缀表达式转换成后缀式(逆波兰表达式)
- 从左到右读中缀表达式的每个字符。
- 如果读到的字符为操作数,则直接输出到后缀表达式中。
- 如果遇到“)”,则弹出栈内的运算符,直到弹出到一个“(”,两者相互抵消。("("不打印)
- “(”的优先级在栈内比任何运算符都小,任何运算符都可以压过它,不过在栈外却是优先级最高者。
- 当运算符准备进入栈内时,必须和栈顶的运算符比较,如果外面的运算符优先级高于栈顶的运算符的优先级,则压栈;如果优先级低于或等于栈顶的运算符的优先级,则弹栈。直到栈顶的运算符的优先级低于外面的运算符优先级或者栈为空时,再把外面的运算符压栈。
- 中缀表达式读完,如果运算符栈不为空,则将其内的运算符逐一弹出,输出到后缀表达式中。
//比较l的优先级是否不高于r,r表示栈顶的符号 bool priority(const char &l, const char &r) { if (r == '(')//左括号在栈外优先级最高 return false; if (l == '+' || l == '-') return true; if ((l == '*' || l == '/') && (r == '*' || r == '/')) return true; return false; } //将中缀表达式转换成后缀式(逆波兰表达式) string change(const string &str) { vector<char> vec; string res; stack<char> st;//操作符栈 for (int i = 0; i < str.length(); i++) { //如果是数字,直接输出到后序表达式中 if (isdigit(str[i])) { vec.push_back(str[i]); } else {//如果是符号,需要与栈顶的元素进行比较 if (st.empty() || str[i] == '(')//小括号在栈外优先级最高,直接压栈 st.push(str[i]); else { //遇到右括号开始弹栈,直至左括号,两者相互抵消 if (str[i] == ')') { while (!st.empty() && st.top() != '(') { vec.push_back(st.top()); st.pop(); } st.pop();//左括号不输出 } else {//遇到的是其他操作符 if (priority(str[i], st.top())) {//优先级比栈顶元素低 while (!st.empty()) { vec.push_back(st.top()); st.pop(); } st.push(str[i]); } else {//优先级比栈顶元素高,压栈 st.push(str[i]); } } } } } while (!st.empty()) {//如果栈不为空,则将其中的元素全部弹出{ vec.push_back(st.top()); st.pop(); } for (auto v : vec) res += v; return res; }
3、后缀表达式求值
后缀表达式具有和前缀表达式类似的好处,没有优先级的问题。
- 直接读取表达式,如果遇到数字就压栈。
- 如果遇到运算符,就弹出两个数进行运算,随后再将运算结果压栈。
double operate(double a, double b, char op) { double res = 0; switch (op) { case '+': res = a+b;break; case '-': res = a-b;break; case '*': res = a*b;break; case '/': res = a/b;break; default: break; } return res; } double calculate(string ss) { stack<double> st;//操作数堆栈 for (auto &s : ss) { //如果是数字就压栈 if (isdigit(s)) { st.push(s - '0'); } else {//遇到字符就弹出两个操作数进行运算 double a = st.top(); st.pop(); double b = st.top(); st.pop(); st.push(operate(b, a, s)); } } return st.empty() ? 0 : st.top();//最后的结果为栈顶元素 }
主函数测试
int main() { string s = "(6+3)*7-5/2"; string ss = change(s); cout << ss << endl; cout << calculate(ss) << endl; }