问题描述:
这里的算数运算求值问题是:用户输入包含运算符“+”,“-”,“*”,“/”,“(”,“)”及正整数构成的合法算数运算式(即中缀式)例如:4+(2*5-6/12),计算并输出该表达式的运算结果。
问题分析:
这是数据结构中堆栈的经典应用,考察栈的基本操作,及一些多分枝结构的代码编写,同时还涉及到表达式中运算符的一些逻辑问题。
这里需要先定义两个栈,一个运算符栈op,一个运算数栈num,在代码中直接使用C/C++的标准库函数来声明(就可不用管栈的具体操作,减少代码量),就需要添加头文件及标准命名空间:
#include<stack>
using namespace std;
在看代码之前,需要先定义运算符的优先级(平时口算时其实也是遵循了“从左到右,先乘除后加减,先括号里后括号外的优先原则”,这里只是把其量化)。同时运算符要区分左右,因为会比较 当前扫描到的运算符(即右运算符) 与 运算符栈顶的运算符(即左运算符) 的优先级孰高孰低
运算符 | = | ( | + | - | * | / | ) |
---|---|---|---|---|---|---|---|
左运算符 | 0 | 1 | 3 | 3 | 5 | 5 | 6 |
右运算符 | 0 | 6 | 2 | 2 | 4 | 4 | 1 |
说明 | 等于 | 左括号 | 加 | 减 | 乘 | 除 | 右括号 |
//初始化运算符栈op、运算数栈num;
//将“=”入运算数符(这只是为了方便操作);
//黑框输入一个中缀式子;
/*以下循环逐个扫描输入的中缀式 */
/*while(中缀式未扫描完)
{
if(当前扫描字符为操作符)
{
if(当前扫描操作符 优先级 大于 op栈顶元素 优先级)
当前操作符入栈op;
读取下一个字符;
else if(当前扫描操作符 优先级 等于 op栈顶元素 优先级)
退op栈顶元素(其实这种情况只有可能是右括号遇到左括号)
else 小于
运算数栈num连续出两个元素,运算符栈op出栈顶元素,进行相应运算;
运算结果再入运算数栈num;
}
else 当前扫描的字符为数字
数字直接入运算数栈num;
}
扫描完毕,输出结果;
*/
直接看代码吧:
#include<stdio.h>
#include<stack>
using namespace std;
#define MaxOp 7 //操作符的个数
struct //运算符优先级结构体
{
char cu_opchar;
int priority;
}left_priority[]={{'=',0},{'(',1},{'+',3},{'-',3},{'*',5},{'/',5},{')',6}},
right_priority[]={{'=',0},{'(',6},{'+',2},{'-',2},{'*',4},{'/',4},{')',1}};
bool IsOperatorChar(char ch) //判断是运算符还是数字
{
if(ch=='('||ch=='+'||ch=='-'||ch=='*'||ch=='/'||ch==')')
return true;
else return false;
}
int Get_LeftPriority(char cu_opchar) //求左运算符优先级
{
for (int i = 0; i < MaxOp; i++)
if (left_priority[i].cu_opchar == cu_opchar) return left_priority[i].priority;
}
int Get_RightPriority(char cu_opchar) //求右运算符优先级
{
for (int i = 0; i < MaxOp; i++)
if (right_priority[i].cu_opchar == cu_opchar) return right_priority[i].priority;
} //
int JudgePriority(char left_ch,char right_ch) //判左右优先级高级
{
int left_p = Get_LeftPriority(left_ch);
int right_p = Get_RightPriority(right_ch);
if (left_p < right_p) return -1;
else if(left_p == right_p) return 0;
else return 1;
}
float Calculator(float first_num,float second_num,char tmp_ch) //计算逻辑
{
float sum;
switch (tmp_ch)
{
case '+': sum = first_num+second_num;
break;
case '-': sum = first_num-second_num;
break;
case '*': sum = first_num*second_num;
break;
case '/':
if (second_num==0)
{
printf("错误:除数为0!\n");exit(0);
}
sum = first_num/second_num;
break;
default: printf("错误:无效的表达式!\n");exit(0);
break;
}
return sum;
}
int main()
{
stack<char> op; //运算符栈
stack<float> num; //运算数栈
int cu_char_no;
float first_num,second_num,sum;
float tmp_num;
char cu_char;
char op_topch;
char in_str[201];
char tmp_ch;
while (scanf("%s",in_str)!=EOF)
{
//清空操作符栈和数字栈
while (!op.empty()) op.pop();
while (!num.empty()) num.pop();
cu_char_no =0;
//将“=”入操作符栈
op.push('=');
//while挨个从输入读取字符,直至输入字符串读取完毕
while (in_str[cu_char_no]!='\0')
{
cu_char =in_str[cu_char_no];
//若当前字符为操作符
if (IsOperatorChar(cu_char))
{
int p = JudgePriority(op.top(),cu_char);
switch (p)
{
case -1: //当前字符优先级大于操作符栈顶元素的优先级,
op.push(cu_char); //当前符入栈
cu_char_no++; //!!!读取下一个字符
break;
case 0: //当前优先级等于栈顶(只有左括号遇到右括号情况)
op.pop(); //操作符栈退栈顶元素
cu_char_no++; //!!!读取下一个字符
break;
default: //(当前优先级小于栈顶元素)
tmp_ch = op.top(); //操作符栈顶元素出栈,数字栈出两元素进行运算
op.pop();
first_num= num.top();num.pop();
second_num= num.top();num.pop();
sum= Calculator(second_num,first_num,tmp_ch);
num.push(sum); //结果入数字栈
break;
}
}
//当前字符为数字
else
{
tmp_num = 0;
tmp_ch =in_str[cu_char_no];
while (tmp_ch>='0'&&tmp_ch<='9')
{
tmp_num=tmp_num*10 +tmp_ch-'0';
tmp_ch =in_str[++cu_char_no];
}
//数字处理,结果入数字栈
num.push(tmp_num);
}
}
//处理完毕,输出结果
while (!op.empty())
{
tmp_ch = op.top();op.pop();
if (tmp_ch == '=')
{
printf("计算结果为:%.2f\n",num.top());break;
}
else
{
first_num= num.top();num.pop();
second_num= num.top();num.pop();
sum= Calculator(second_num,first_num,tmp_ch);
num.push(sum); //结果入数字栈
}
}
}
return 0;
}
黑框框输入及输出:
写一写的就写了小150行的代码,代码还比较啰嗦。以上算法及思路参考了李春葆的《数据结构教材》第四版,但他是把
中缀式先转化成后缀式,然后再用后缀式计算结果的。
while (scanf("%s",in_str)!=EOF)
上面这句在ACM中见的比较多,是用于多次进行输入待处理数据的。
tmp_num = 0;
tmp_ch =in_str[cu_char_no];
//数字处理,
while (tmp_ch>='0'&&tmp_ch<='9')
{
tmp_num=tmp_num*10 +tmp_ch-'0';
tmp_ch =in_str[++cu_char_no];
}
//结果入数字栈
num.push(tmp_num);
上面的while循环是用于将chan类型的任意位的数字字符转换成float(或者是int)的数字