我们平常使用的式子都是中缀表达式。
在程序中,我们可以利用栈,将中缀表达式转换为后缀表达式。也利用栈,将后缀表达式计算出结果。
①中缀表达式转后缀表达式
如1+2*3/(3-1) ---> 1 2 3 * 3 1 - / +
转换的思想:
一个空的后缀表达式
一个存放符号的栈s
在转换的过程中,需要加入空格以区分不同数字与数字,数字与符号。(具体代码中体现)
字符 | 具体操作 |
数字 | 直接输出至后缀式中 |
字符 '(' | 将 '(' 放入符号栈s当中 |
字符 ')' | 将栈s中直到第一个 '(' 的前的符号都输出到后缀表达式中,并且 |
运算符:栈外优先级 > 栈内优先级 | 此运算符直接入栈 |
运算符:栈外优先级 <= 栈内优先级 | 将栈顶符号出栈输出到后缀式中,此运算符入栈 |
最后把栈内剩余的符号输出至后缀表示式
操作符优先级
# | 0 |
( | 1 |
) | 6 |
+ - | 2 |
* / | 3 |
例子:2+3*(4-2) 式子共9个运算符
2是数字,直接放到后缀表达式: 2 (后缀表达式) | #(栈)
+是符号,'+' > '#' ,直接入栈: 2 | # +
3是数字,直接放到后缀表达式: 2 3 | # +
*是符号,'*' > '+' ,直接入栈: 2 3 | # + *
(是符号,'(' , 直接入栈: 2 3 | # + * (
4是数字,直接放到后缀表达式: 2 3 4 | # + * (
-是符号,'-' > '(' , 直接入栈: 2 3 4 | # + * ( -
2是数字,直接放到后缀表达式: 2 3 4 2 | # + * (
)是符号,直接将(后的输出 : 2 3 4 2 - | # + *
最后,将栈内除了#的所有符号输出到后缀中。2 3 4 2 - * +
②计算后缀表达式的结果
将后缀表达式一一入栈
入栈是数字时,直接放进去
入栈是符号时,将栈顶的两个数拿出来进行计算(注意是被减数还是减数,被除数还是除数),并将结果放回至栈顶
最后遍历完,将栈内剩下的唯一结果输出。
#include <iostream>
#include <stack>
#include <math.h>
using namespace std;
bool Number(char ch)
{
if(ch>=48 && ch<=57)
return true;
else
return false;
}
void Input(char*& str){ //输入表达式,并且检测是否正确
cout<< "请输入想要转换的表达式" <<endl;
while(1){
cin >> str;
if(Number(str[0]))
{
break;
}
else
{
cout<<"输入的表达式格式有误,请重新输入"<<endl;
delete[] str;
}
}
}
void AddSpace(char*& arr)
{
*arr = ' ';
arr++;
}
int GetPriority(char a)
{
switch(a)
{
case '#':
return 0;
case '(':
return 1;
case '+':
return 2;
case '-':
return 2;
case '*':
return 3;
case '/':
return 3;
default:
return -1;
}
}
char* GetRevSequence(){ //将input表达式转为后缀表达式
char* str_ori = new char[30];
char* str_res = new char[30];
char* str_record;
stack<char> s;
s.push('#');
Input(str_ori);
str_record = str_res;
while(*str_ori)
{
cout<<*str_ori<<endl;
if(Number(*str_ori)) //如果str_ori是数字,直接放到后缀序列里
{
*str_res = *str_ori;
*str_res++;
*str_ori++;
}
else//如果是操作符
{
if(Number(*(str_ori-1)))//如果前一位是数字的话,就加空格,来分隔数字
{
AddSpace(str_res);
}
if(*str_ori=='(') //如果是'(',直接入栈
{
s.push(*str_ori);
str_ori++;
}
else if(*str_ori==')')//如果是')',则将栈中第一个'('前的都输出,并且将)出栈
{
while(s.top()!='(')
{
*str_res=s.top();
s.pop();
// str_ori++;
str_res++;
AddSpace(str_res);
}
str_ori++; //注意这里是在循环外,str_ori加一
s.pop();
}
else if(GetPriority(*str_ori) <= GetPriority(s.top())) //栈外小于等于栈内,把栈内输出后,把栈外放入栈内
{
*str_res = s.top();
s.pop();
s.push(*str_ori);
str_res++;
str_ori++;
AddSpace(str_res);//这里是要加个空格的,每次加数字不用加空格,但是如果判断是符号,则加了一个空格,
//然后如果这个符号要在这里输出,就要在后面同样再接上一个空格,因为此符号后面只可能接数字和(
//如果是'('的话,绕过所有'('后,还是只会接数字,所以最终,符号出栈后面必然是数字,所以需要加空格
}
else if(GetPriority(*str_ori) > GetPriority(s.top())) //栈外大于栈内,则直接入栈
{
s.push(*str_ori);
str_ori++;
}
}
}
while(s.top()!='#')//将栈中所有除#符号输出
{
//最后遍历,只可能是以 '数字+)' 或 数字 结尾(本质都是数字结尾),所以剩余的符号在加入时需提前加入空格
AddSpace(str_res);
*str_res = s.top();
cout<<s.top()<<endl;
s.pop();
str_res++;
}
*str_res='\0';
cout<<"中缀表达式是: "<<str_record<<endl;
return str_record;
}
double GetNumber(char*& arr) //从字符串中提取出单个数字
{
int n=0;//用来记录此数的位数 ————————————那么带小数点的怎么弄?
double sum[20];
double result = 0;
while(Number(*arr))
{
sum[n] = *arr - 48;
n++;
arr++;
}
int k = n-1;
for(int i=0;i<n;i++,k--)
{
result += sum[i] * pow(10,k);
}
return result;
}
double cal(double a, double b, char c) //注意被减数和减数?
{
switch(c)
{
case '+': cout<<b<<c<<a<<endl; return (b + a);
case '-':cout<<b<<c<<a<<endl; return (b - a);
case '*': cout<<b<<c<<a<<endl; return (b * a);
case '/': cout<<b<<c<<a<<endl; return (b / a);
}
cout<<b<<c<<a<<endl;
}
double GetResult(char* str) //从中缀转后缀
{
stack<double> s;
while(*str)
{
if(Number(*str))//如果是数字的话,要将空格间的多位数字提出来
{
s.push(GetNumber(str));
}
else if(*str==' ')
{
str++;
}
else
{
double a = s.top();//右操作数
s.pop();
double b = s.top();//左操作数
s.pop();
s.push(cal(a,b,*str));//把新的结果放进栈里
str++;
}
}
//cout<<s.top()<<endl;
return s.top();
}
int main(){
// cout << "The Result Is: "<<GetRevSequence() <<endl;
cout << "The Result Is: "<<GetResult(GetRevSequence()) <<endl;
//
}