《算法笔记》学习日记——6.7 stack的常见用法详解

目录

  • 6.7 stack的常见用法详解
    • 问题 A: 简单计算器
    • 问题 B: Problem E
    • 小结

6.7 stack的常见用法详解

Codeup Contest ID:100000602

问题 A: 简单计算器

题目描述
读入一个只包含 +, -, *, / 的非负整数计算表达式,计算该表达式的值。
输入
测试输入包含若干测试用例,每个测试用例占一行,每行不超过200个字符,整数和运算符之间用一个空格分隔。没有非法表达式。当一行中只有0时输入结束,相应的结果不要输出。
输出
对每个测试用例输出1行,即该表达式的值,精确到小数点后2位。
样例输入

30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92
0

样例输出

12178.21

思路
这题在用Tkinter写Python计算器的时候已经实现过了,不过Python用的是列表,而在这里我们用STL的stack容器。

不过不管是用什么语言实现,思路肯定还是一致的,开辟两个栈,一个存放数字,一个存放操作符,并且规定操作符之间的优先级,在这里我用了STL的map(因为联想到了Python实现时,用的是字典,字典也是key、value对应,和map一模一样)。

于是,每读入一个数字,就压入数字栈,每碰到一个操作符,就首先判断操作符栈是否为空,如果是空,那就直接入栈,如果非空,就要判断是不是比栈顶操作符的优先级大,如果大,那也入栈,如果小,那就执行运算(注意,这里执行的运算次数可能不止一次,只要读入的操作符优先级小于等于栈顶操作符的优先级,就要一直运算,否则会出错)。

运算的具体步骤就是,从数字栈pop()出两个元素作为a和b(a是栈顶数字,b是次顶数字),然后从操作符栈pop()出一个元素作为操作符,两者进行一次运算,并且把运算好的结果重新压入数字栈。这里要注意的是,减法和除法的话,一定是次顶数字减去栈顶数字(或者次顶数字除以栈顶数字),即:b - a 或者 b / a,否则也是会出错的。

总的来说这题代码可能比较长,但是思路还是简单的,如果程序没有输出(卡一段时间,然后意外中断),那多半是在空栈里面执行了pop()操作,因此一定要注意编写的代码是不是一定在栈非空的情况下才执行pop()。
代码

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
stack<double> num;//数字栈 
stack<char> op;//操作符栈
map<char, int> m;//规定优先级
int main(){
	m['+'] = 0;
	m['-'] = 0;
	m['*'] = 1;
	m['/'] = 1;
	string tmp;
	while(getline(cin, tmp)){
		if(tmp=="0") break;
		string number;
		for(int i=0;i<tmp.length();i++){
			if(tmp[i]==' '){
				if(number.length()!=0){//如果number字符串非空,说明空格之前是数字 
					double temp = stod(number);//用stod()函数把string型转成double型
					num.push(temp);//把之前的数字入栈
					number.clear();//清空字符串,准备接收下一个字符	
				}
				continue;//继续下一轮循环 
			}
			if(tmp[i]=='+'||tmp[i]=='-'||tmp[i]=='*'||tmp[i]=='/'){
				if(op.empty()==true||m[tmp[i]]>m[op.top()]){
					op.push(tmp[i]);//如果是空栈或者即将压入的操作符优先级大于栈顶操作符,则把当前的操作符入栈 
				}
				else{
					while(m[tmp[i]]<=m[op.top()]){//如果即将压入的操作符的优先级小于等于栈顶操作符的优先级
						//则进行运算,并入栈 
						double a = num.top();//数字栈顶元素 
						num.pop();
						double b = num.top();//数字栈次顶元素 
						num.pop();
						char c = op.top();
						op.pop();
						if(c=='+'){
							double temp = a+b;
							num.push(temp);//记得把算好的结果入栈 
						} 
						else if(c=='-'){
							double temp = b-a;
							num.push(temp);
						}
						else if(c=='*'){
							double temp = a*b;
							num.push(temp);
						}
						else{
							double temp = b/a;
							num.push(temp);
						}
						if(op.empty()==true) break;//如果操作符栈为空,则break,否则判断会出错 
					}
					op.push(tmp[i]);//运算完了之后压栈 
				}
				continue;
			}
			if(i==tmp.length()-1){//如果读到了字符串的尾部,就进行所有运算,直到操作符栈清空 
				number += tmp[i];//把最后一个数字字符串上 
				double temp = stod(number);//用stod()函数把string型转成double型
				num.push(temp);//把最后的数字入栈
				number.clear();//清空字符串
				while(!op.empty()){//只要操作符非空,就一直运算 
					double a = num.top();//数字栈顶元素 
					num.pop();
					double b = num.top();//数字栈次顶元素 
					num.pop();
					char c = op.top();
					op.pop();
					if(c=='+'){
						double temp = a+b;
						num.push(temp);//记得把算好的结果入栈 
					} 
					else if(c=='-'){
						double temp = b-a;
						num.push(temp);
					}
					else if(c=='*'){
						double temp = a*b;
						num.push(temp);
					}
					else{
						double temp = b/a;
						num.push(temp);
					}
				}
				break;
			}
			number += tmp[i];
		}
		double sum = num.top();
		num.pop();
		cout<<fixed<<setprecision(2)<<sum<<endl;
	}
	return 0;
}

问题 B: Problem E

题目描述
请写一个程序,判断给定表达式中的括号是否匹配,表达式中的合法括号为”(“, “)”, “[", "]“, “{“, ”}”,这三个括号可以按照任意的次序嵌套使用。
输入
有多个表达式,输入数据的第一行是表达式的数目,每个表达式占一行。
输出
对每个表达式,若其中的括号是匹配的,则输出”yes”,否则输出”no”。
样例输入

4
[(d+f)*{}]
[(2+3))
()}
[4(6]7)9

样例输出

yes
no
no
no

思路
这题相比于上一题来说的话简单多了,思路就是只要碰到左括号就入栈,如果碰到右括号的话,就分情况处理:①如果此时栈是空的,那么毫无疑问直接入栈;②如果不匹配栈顶的左括号,那么也入栈;③如果匹配,那就直接pop()掉栈顶的左括号。
因此,如果操作完一个字符串之后,栈是空的,就说明括号匹配,如果栈不是空的,就说明不匹配。
代码

#include
#include
#include
#include
#include
#include
using namespace std;
int main(){
	int n;
	while(cin>>n){
		for(int i=0;i<n;i++){
			stack<char> kuohao;//存放括号 
			string tmp;
			cin>>tmp;
			for(int j=0;j<tmp.length();j++){
				if(tmp[j]=='['||tmp[j]=='('||tmp[j]=='{'){//如果是左括号,入栈
					kuohao.push(tmp[j]); 
				}
				if(tmp[j]==']'||tmp[j]==')'||tmp[j]=='}'){//如果是右括号,处理括号匹配问题
					if(kuohao.empty()==true) kuohao.push(tmp[j]);//如果是空栈,直接入栈 
					else if(kuohao.top()=='['&&tmp[j]==']') kuohao.pop();//如果匹配,则直接出栈左括号 
					else if(kuohao.top()=='('&&tmp[j]==')') kuohao.pop();
					else if(kuohao.top()=='{'&&tmp[j]=='}') kuohao.pop();
					else kuohao.push(tmp[j]);//如果不匹配,入栈 
				}
			}
			if(kuohao.empty()==true) cout<<"yes"<<endl;//如果处理完字符串之后,栈为空,说明括号匹配
			else cout<<"no"<<endl;//否则就说明不匹配 
		}
	}
	return 0;
}

小结

总的来说,栈的题目无非是让你计算前缀、中缀、后缀表达式的值,或者括号匹配问题,这其中呢,括号匹配问题是比较简单的,但是计算表达式的值就要多花一些力气了,如果思绪稍微不清楚一点,就会碰到对一个空栈top()或者对一个空栈pop()之类的问题,这种时候就要检查代码,是不是逻辑上不够严谨,思考可能出现的特殊情况,以此来完善代码。如果能计算出值,但是答案错误,就说明算法上出现了问题,如果检查不出来,建议把样例画在纸上,按照你程序的思路手动模拟一遍,就能知道错在哪儿了。

做这种题给我的感觉和PAT乙级那道处理A1~A5的问题很像,就是说题目本身不难,思路也很容易想到,就是代码量大,容易出现各种各样的小问题,这个时候一定要静下心来慢慢想,不能烦躁,否则是永远解决不了问题的。

你可能感兴趣的:(《算法笔记》学习日记)