一、前言
最近复习到数据结构的栈和队列的应用部分,学习了一下王道视频里表达式求值部分的思路,特地写了下代码跟大家分享,仅供学习参考。若有错误的地方,请多指教!😄😄😄
二、目录
1、中缀表达式转前缀表达式
2、中缀表达式转后缀表达式
3、前缀表达式求值
4、后缀表达式求值
5、中缀表达式求值
三、正文
1、中缀表达式转前缀表达式
建议先看中缀表达式转后缀表达式,比较好理解一些。
思路
1、从右往左扫描中缀表达式,直到最后一个元素为止:
- 如果遇到操作数。则直接加入前缀表达式。
- 如果遇到运算符。则比较当前运算符和栈顶符号的优先级。
- 如果当前运算符的优先级大于栈顶符号的优先级,则直接将该运算符压入栈中;
- 否则,依次弹出栈中所有优先级大于等于当前运算符的所有运算符,并加入到前缀表达式。最后把当前运算符压入栈中。
- 如果遇到界限符。
- 遇到右括号“)”,则直接将右括号压入栈中;
- 遇到左括号“(”,则依次弹出栈内运算符并加入前缀表达式,直到弹出右括号“)”为止。注意“)”不加入前缀表达式。
2、最后,将栈中剩余的运算符依次弹出,并加入到前缀表达式。注意此时得到的是前缀表达式的逆序,我们需要将结果倒序一下,大功告成!😁😁😁
注意:括号的优先级应该设为最低噢!
代码
//规定优先级
int level(char s){
if(s=='+' || s=='-')
return 1;
else if (s=='*'||s=='/')
return 2;
//设括号优先级为0
else return 0;
}
//中缀表达式转前缀表达式
char* infix_to_prefix(char *str){
//用栈结构,将字符串倒序,以符合“从右往左”原则。
stack<char> s;
int i=0;
while(str[i]!='\0'){
s.push(str[i]);
++i;
}
//定义一个栈来保存结果;
stack<char> result;
//定义一个栈来作为符号栈
stack<char> symbol_stack;
while(!s.empty()){
char temp=s.top();
s.pop();
//--------------数字处理------------------
if(temp>='0'&&temp<='9'){
result.push(temp);
}
//--------------运算符处理-----------------
else if(temp=='+'||temp=='-'||temp=='*'||temp=='/'){
//如果符号栈为空 或者 扫描到的 运算符的 优先级 > 符号栈的 栈顶符号的 优先级
if(symbol_stack.empty()||level(temp)> level(symbol_stack.top())){
symbol_stack.push(temp);
}
//如果扫描到的 运算符的 优先级 <= 符号栈的 栈顶符号的 优先级
else if(level(temp)<= level(symbol_stack.top())){
//弹出 优先级 高于或等于 当前运算符优先级的 所有符号
while(!symbol_stack.empty()&&level(symbol_stack.top())>= level(temp)){//先判空再用top()不然会报错!
result.push(symbol_stack.top());
symbol_stack.pop();
}
//将当前符号压入栈中
symbol_stack.push(temp);
}
}
//----------------界限符处理--------------------
else{
//遇到右括号,直接进栈
if(temp==')'){
symbol_stack.push(temp);
}
//遇到左括号,依次弹出符号栈内运算符,知道遇到右括号
else{
while (symbol_stack.top()!=')'){
result.push(symbol_stack.top());
symbol_stack.pop();
}
symbol_stack.pop();//将')'也出栈
}
}
}
//将符号栈内剩余符号全部取出
while(!symbol_stack.empty()){
result.push(symbol_stack.top());
symbol_stack.pop();
}
//将结果赋值到字符串数组
int j=0;
char *res=new char[20];
while(!result.empty()){
res[j++]=result.top();
result.pop();
}
//加入字符串结束字符'\0'
res[j]='\0';
return res;
}
结果
2、中缀表达式转后缀表达式
思路
1、从左往右扫描中缀表达式,直到最后一个元素为止:
- 如果遇到操作数。则直接加入后缀表达式。
- 如果遇到运算符。则比较当前运算符和栈顶符号的优先级。
- 如果当前运算符的优先级大于栈顶符号的优先级,则直接将该运算符压入栈中;
- 否则,依次弹出栈中所有优先级大于等于当前运算符的所有运算符,并加入到后缀表达式。最后把当前运算符压入栈中。
- 如果遇到界限符。
- 遇到左括号“(”,则直接将左括号压入栈中;
- 遇到右括号“)”,则依次弹出栈内运算符并加入后缀表达式,直到弹出左括号“(”为止。注意“(”不加入后缀表达式。
2、最后,将栈中剩余的运算符依次弹出,并加入到后缀表达式。大功告成!😁😁😁
注意:括号的优先级应该设为最低噢!
代码
//规定优先级
int level(char s){
if(s=='+' || s=='-')
return 1;
else if (s=='*'||s=='/')
return 2;
//设括号优先级为0
else return 0;
}
//中缀表达式转后缀表达式,返回值为字符串
char* infix_to_postfix(char* str){
char*res=new char [20];//用来存放结果
int index=0;
stack<char> stack;
for(int i=0;str[i]!='\0';++i){
//--------------数字处理---------------------------
if('0'<=str[i]&&str[i]<='9'){
res[index++]=str[i];
}
//--------------运算符处理-------------------------
else if (str[i]=='+'||str[i]=='-'||str[i]=='*'||str[i]=='/'){
//扫描到的 符号的 优先级 > 栈顶符号的 优先级 或者 符号栈为空,直接进栈
if(stack.empty() ||level(stack.top())< level(str[i]))
stack.push(str[i]);
//扫描到的符号的优先级 <= 栈顶符号的 优先级
else{
//弹出栈中优先级高于或等于当前运算符的 所有 运算符
while(!stack.empty()&&level(stack.top())>= level(str[i])){ //先判空再用stack.top()不然会报错!
res[index++]=stack.top();
stack.pop();
}
stack.push(str[i]); //最后把扫描到的字符压入栈中
}
}
//---------------界限符处理----------------------
else{
if(str[i]=='(') //遇到左括号'(',直接进栈
stack.push(str[i]);
else{ //遇到右括号')'依次弹出栈内运算符,直到遇到左括号'('
while(stack.top()!='('){
res[index++]=stack.top();
stack.pop();
}
stack.pop();//左括号也出栈
}
}
}
//将栈中所有剩余字符送入表达式
while(!stack.empty()){
res[index++]=stack.top();
stack.pop();
}
//加入字符串结束字符'\0'
res[index]='\0';
return res;
}
结果
3、前缀表达式求值
思路
1、从右往左描表达式中的每个元素,直到最后一个元素为止;
- 如果扫描到操作数,则直接进栈;
- 如果扫描到运算符,则弹出两个栈顶元素(先弹出的是左操作数,后弹出的右操作数),执行相应运算并压回栈中;
2、当表达式扫描结束,栈顶元素便是计算结果。大功告成!😁😁😁
代码
//两数的四则运算
double value(double op1,double op2,char ch){
switch (ch) {
case '+': return op1+op2;break;
case '-': return op1-op2;break;
case '*': return op1*op2;break;
case '/': return (op1*1.0)/op2;break;
}
}
//前缀表达式求值
double calculate(char* str){
stack<char> s;
//将表达式倒序
int i=0;
while(str[i]!='\0'){
s.push(str[i]);
++i;
}
stack<double> result;
//倒序遍历后缀表达式
while(!s.empty()){
char temp=s.top();
s.pop();
if(result.empty()||temp>='0'&&temp<='9')
result.push(temp-'0');
else{
double op1=result.top();
result.pop();
double op2=result.top();
result.pop();
result.push(value(op1,op2,temp));
}
}
return result.top();
}
结果
4、后缀表达式求值
思路
1、从左往右扫描表达式中的每个元素,直到最后一个元素为止;
- 如果扫描到操作数,则直接进栈;
- 如果扫描到运算符,则弹出两个栈顶元素(先弹出的是右操作数,后弹出的左操作数),执行相应运算并压回栈中;
2、当表达式扫描结束,栈顶元素便是计算结果。大功告成!😁😁😁
代码
//两数的四则运算
double value(double op1,double op2,char ch){
switch (ch) {
case '+': return op1+op2;break;
case '-': return op1-op2;break;
case '*': return op1*op2;break;
case '/': return (op1*1.0)/op2;break;
}
}
//后缀表达式计算
double calculate(char*str){
stack<double> stack;
double res=0;
//遍历后缀表达式
for(int i=0;str[i]!='\0';++i){
if (str[i]>='0'&&str[i]<='9'){
stack.push(str[i]-'0');
}
else{
double op2=stack.top();
stack.pop();
double op1=stack.top();
stack.pop();
double temp=value(op1,op2,str[i]);
stack.push(temp);
res+=temp;
}
}
return stack.top();
}
int main(){
//infix_to_postfix函数为上面中缀转后缀的函数
cout<<calculate(infix_to_postfix("5/1+(9-2)*4/2"));
return 0;
}
结果
5、中缀表达式求值
思路
中缀表达式求值,其实就是先将中缀表达式转换成后缀表达式(前缀表达式),再通过后缀表达式(前缀表达式)的求值方法来计算。
当然你也可以把这两个步骤合二为一,便是以下代码所给出的内容。如果你已经掌握了中缀表达式转后缀表达式并计算的思路的话,下面的代码你应该看得懂,这里就不再赘述思路了。(懒得写了😂😂😂)
代码
//规定优先级
int level(char s){
if(s=='+' || s=='-')
return 1;
else if (s=='*'||s=='/')
return 2;
//设括号优先级为0
else return 0;
}
//四则运算
double value(double op1,double op2,char ch){
switch (ch) {
case '+': return op1+op2;break;
case '-': return op1-op2;break;
case '*': return op1*op2;break;
case '/': return (op1*1.0)/op2;break;
}
}
//中缀表达式求值
double calculate_infix(char* str){
//数字栈
stack<double> number_stack;
//符号栈
stack<char> symbol_stack;
for(int i=0;str[i]!='\0';++i){
//------------------------如果扫描到数字---------------------
if(number_stack.empty()||str[i]>='0'&&str[i]<='9'){
number_stack.push(str[i]-'0');
}
//------------------------如果扫描到运算符-------------------
else if(str[i]=='+'||str[i]=='-'||str[i]=='*'||str[i]=='/'){
//如果当前运算符的优先级>栈顶符号优先级,则直接进栈
if(symbol_stack.empty()|| level(str[i])> level(symbol_stack.top()))
symbol_stack.push(str[i]);
//如果当前运算符的优先级<=栈顶符号的优先级
else{
//依次弹出优先级高于或等于当前符号的所有符号,并同时弹出数字栈中的两个数字作为操作数。注意先后顺序。
while(!symbol_stack.empty()&&level(symbol_stack.top())>= level(str[i])){
double op2=number_stack.top();
number_stack.pop();
double op1=number_stack.top();
number_stack.pop();
//将计算结果压入数字栈
number_stack.push(value(op1,op2,symbol_stack.top()));
//符号出栈
symbol_stack.pop();
}
symbol_stack.push(str[i]);//最后把当前符号入栈
}
}
//-----------------如果扫描到界限符-------------------
else{
if(str[i]=='(')
symbol_stack.push(str[i]);
else{
//遇到右括号')'依次弹出符号栈内运算符,和数字栈内的两个运算符,做计算,直到遇到左括号'('为止
while(symbol_stack.top()!='('){
double op2=number_stack.top();
number_stack.pop();
double op1=number_stack.top();
number_stack.pop();
//将计算结果压入数字栈
number_stack.push(value(op1,op2,symbol_stack.top()));
//符号出栈
symbol_stack.pop();
}
symbol_stack.pop();
}
}
}
//将符号栈所有运算符出栈,与数字栈剩余数字做运算
while(!symbol_stack.empty()){
double op2=number_stack.top();
number_stack.pop();
double op1=number_stack.top();
number_stack.pop();
//将计算结果压入数字栈
number_stack.push(value(op1,op2,symbol_stack.top()));
//符号出栈
symbol_stack.pop();
}
return number_stack.top();
}
结果
四、总结
应该没什么人会在意总结吧,随便写写咯,指出一下不足之处吧。😁😁😁
不足之处就是,只能计算一位整数的加减乘除,小数呀、两位数啥的都不行。🙄🙄🙄
欢迎指教😛😛😛