/*STL 中栈的使用方法(stack)
基本操作:
#include <stack>
stack <char> s
s.push(x) 将x加入栈中,即入栈操作
s.pop() 出栈操作(删除栈顶),只是出栈,没有返回值
s.top() 返回第一个元素(栈顶元素)
s.size() 返回栈中的元素个数
s.empty() 当栈为空时,返回 true
*/
//下边为百度百科给的算法详细解释
/*将一个普通的中序表达式转换为逆波兰表达式的一般算法是:
首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为输入逆波兰式的栈S2(空栈),
S1栈可先放入优先级最低的运算符#,注意,中缀式应以此最低优先级的运算符结束。
可指定其他字符,不一定非#不可。从中缀式的左端开始取字符,逐序进行如下步骤:
(1)若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈
(2)若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,
如果该运算符优先级大于S1栈栈顶运算符优先级,则将该运算符进S1栈,
否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级,
最后将该运算符送入S1栈。
(3)若取出的字符是“(”,则直接送入S1栈顶。
(4)若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。
(5)重复上面的1~4步,直至处理完所有的输入字符
(6)若取出的字符是“#”,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。
完成以上步骤,S2栈便为逆波兰式输出结果。不过S2应做一下逆序处理。便可以按照逆波兰式的计算方法计算了!*/
/*计算方法:
新建一个表达式, 如果当前字符为变量或者为数字,则压栈,如果是运算符,
则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。*/
/*我的算法理解:
第一大步:把中缀表达式转化为后缀表达式。
扫中缀式:
1.数字:直接取出进数组ep
2.左括号:进栈op
3.右括号:从栈顶扫栈op,若不是左括号就出栈,加到数组ep中,直到出现左括号,把左括号出栈
4.等号:将栈op中除'#'的元素全部输出
5.运算符:比较栈顶与当前运算符优先级,在栈顶优先级高于当前运算符优先级时,栈顶不断出栈,
加到数组ep中,否则,跳出循环,把当前运算符入栈。
第二大步:根据中缀表达式求值。
1.遇到数字:直接进栈num
2.遇到运算符:对栈顶的两个元素进行出栈并运算,结果入栈num
3.遇到空格:自加,continue
4.遇到回车:直接跳出结束。
*/
//输入要求:可以含有数字,小数点,运算符,以等号结束,最后加回车,不含有空格
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <stack>
#define N 1000
using namespace std;
stack <char> op;//存字符的栈op
stack <double> num;//存储double数据的栈
int i,len,j;
char ch, str[N],ep[N];//str数组存储输入的字符串,ep数组用于存储后缀表达式
double calc(double a, char o, double b)//具体两个元素的计算
{
switch (o)
{
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
case '^': return pow(a,b);
}
}
int pri(char ch)//根据符号返回优先级
{
switch (ch)
{
case '#': return -1;
case '+':
case '-': return 1;
case '*':
case '/': return 2;
case '^': return 3;
case '(': return 0;
}
}
void getnum()//把str中的数字串存到ep中
{
while (((str[i] >= '0' && str[i] <= '9') || str[i] == '.') && i < len)
ep[j++] = str[i++];
ep[j++] = ' ';
return;
}
double getnum2()//把ep中的一个数字以double型返回出来
{
j = 0;
char s[N];
while (((ep[i] >= '0' && ep[i] <= '9') || ep[i] == '.') && i < len)
s[j++] = ep[i++];
s[j] = '\0';
return atof(s);
}
void back()//op栈顶元素出栈,并进入ep中
{
char c = op.top(); op.pop();
ep[j++] = c;
ep[j++] = ' ';
return;
}
void trans()//求后缀表达式
{
while (i < len)
{
if ((str[i] >= '0' && str[i] <= '9'))//如果为数字
{
getnum();
continue;
}
switch (str[i])
{
case '(': op.push(str[i]); i++; break;//左括号的进栈
case ')'://遇到右括号,把op栈中所有符号赋给数组ep,直到遇到左括号为止
while (op.top() != '(')
back();
i++;
op.pop();
break;
case '='://遇到等号将栈op中剩余符号放入ep中,除'#'外;
while (op.top() != '#')
back();
i++;
break;
default ://遇到运算符,先把栈中比当前优先级高的出栈并放入ep中,
//直至遇到优先级较低的,把当前元素进栈
while (pri(str[i]) <= pri(op.top()))
back();
op.push(str[i]);
i++;
break;
}
}
}
double cal()
{
while (i < len)
{
if (ep[i] == ' ')//空格
{
i++;
continue;
}
if (ep[i] == '\n')//换行
{
return num.top();
}
if (ep[i] >= '0' && ep[i] <= '9')//数字
{
num.push(getnum2());
i++;
}
else//运算符
{
double b = num.top(); num.pop();
double a = num.top(); num.pop();//把num栈顶连个元素出栈
num.push(calc(a, ep[i], b));//把计算结果入栈num
i++;
}
}
}
int main()
{
i = 0;
while(scanf("%c", &ch) && ch!='\n')//输入字符串存入str
str[i++] = ch;
len = i;//记录数组str长度
printf("原表达式为: ");
for (int k = 0; k < len-1; k++)
printf("%c", str[k]);
printf("\n");
i = 0;
j = 0;
while (!op.empty()) op.pop();
while (!num.empty()) num.pop();
op.push('#');//初始化i,j和栈op,num
trans();
j--;
ep[j] = '\n';//把数组ep最后一位的空格换回回车
printf("逆波兰式为: ");
for (int k = 0; k <= j; k++)//遍历输出ep,即为逆波兰式
printf("%c", ep[k]);
//求出后缀表达式之后开始对其计算求值,下边先初始化变量
len = j+1;//数组ep的长度为len
i = 0;
j = 0;
printf("最终结果为: %.6lf\n", cal());
return 0;
}
制作计算器,包含后缀表达式的求法
最新推荐文章于 2022-11-09 21:13:33 发布