- 实验项目名称
栈和队列
- 实验内容
表达式求值运算是实现程序设计语言的基本问题之一,也是栈应用的一个典型例子。设计并演示用算符优先级对算术表达式的求解过程。
- 算法分析
typedef struct operands {
double data;
struct operands* next = NULL;
}ANDS;
typedef struct operators {
char data;
struct operators* next = NULL;
}ATORS;
ANDS* Push(double data, ANDS* ands) {
ANDS* p = new ANDS;
p->data = data;
p->next = ands;
return p;
}
ATORS* Push(char data, ATORS* ators) {
ATORS* p = new ATORS;
p->data = data;
p->next = ators;
return p;
}
double Pop(ANDS*& p) {
ANDS* q = new ANDS;
q = p;
double e = q->data;
p = p->next;
free(q);
return e;
}
char Pop(ATORS*& p) {
ATORS* q = new ATORS;
q = p;
char e = p->data;
p = p->next;
free(q);
return e;
}
double Top(ANDS* p) {
return p->data;
}
char Top(ATORS* p) {
return p->data;
}
栈的定义,以及对栈的基本操作:将元素压入栈,将元素出栈并返回栈顶元素,返回栈顶元素。时间复杂度都为O(1)。
string Transtack(string str) {// 将运算表达式转化为后缀表达式
string s;
if (str[str.length() - 1] != '=') {
str += '=';
}
ATORS* OPTR = new ATORS;// 运算符栈
OPTR = Push('#', OPTR);// '#'为栈底
for (int i = 0; ; i++) {
char c;
switch (str[i]) {
case '(':
OPTR = Push(str[i], OPTR);
s += ' ';
break;
case ')':
c = Pop(OPTR);
if (c == '(') {
s += ' ';
}
while (c != '(') {
s += c;
c = Pop(OPTR);
}
break;
case '+':
case '-':
c = Pop(OPTR);
while (c != '(' && c != '#') {
s += c;
c = Pop(OPTR);
}
OPTR = Push(c, OPTR);
OPTR = Push(str[i], OPTR);
s += ' ';
break;
case '*':
case '/':
c = Pop(OPTR);
while (c == '*' || c == '/') {
s += c;
c = Pop(OPTR);
}
OPTR = Push(c, OPTR);
OPTR = Push(str[i], OPTR);
s += ' ';
break;
case '=':
c = Pop(OPTR);
while (c != '#') {
s += c;
c = Pop(OPTR);
}
return s;
default:// 是操作数
s += str[i];
if (str[i + 1] > '9' || str[i + 1] < '0') {
s += ' ';
}
break;
}
}
}
将运算表达式转化为后缀表达式:
(1) 首先构造一个运算符栈,运算符(以括号为分界点)在栈内遵循越往栈顶优先级不降低的原则进行排列。
(2)从左向右扫描算术表达式,从左边第一个字符开始判断:
a.如果当前字符是字母,则直接输出。
b.如果是运算符(不包括括号),则比较优先级。
①如果是空栈,直接入栈;
②如果当前运算符的优先级大于栈顶运算符的优先级,则将运算符直接入栈;
注:对于优先级相同的运算符来说,先出现的先运算,所以优先级相等的情况是要先将栈顶元素出栈再将当前元素入栈。
③否则将栈顶运算符出栈并输出,直到当前运算符的优先级大于栈顶运算符的优先级or栈顶是左括号时,再将当前运算符入栈。
注:括号比任何在括号前面入栈的元素优先级都大,比任何在左括号之后的,右括号之前的优先级都小。因为括号之中的运算符优先级最大
c.如果是括号,则根据括号的方向进行处理。
①如果是左括号,则直接入栈;
②否则,遇右括号前将所有的运算符全部出栈并输出,遇左括号后将左右的两括号一起删除。
(3) 重复上述操作(2)直至扫描结束,将栈内剩余运算符全部出栈并输出。
double Calculation(string s)
{
s += '#';// 结束标志
ANDS* ands = new ANDS;// 运算数栈
double temp = 0;
for (int i = 0; s[i] != '#'; i++)
{
if (s[i] == ' ') {
temp /= 10;
if (temp == 0) {
continue;
}
ands = Push(temp, ands);
temp = 0;
continue;
}
if (s[i] >= '0' && s[i] <= '9')
{
temp = (temp + int(s[i]) - 48) * 10;
}
else
{
double a, b;
a = Pop(ands);
b = Pop(ands);
if (s[i] == '+')
ands = Push(a + b, ands);
else if (s[i] == '-')
ands = Push(b - a, ands);
else if (s[i] == '*')
ands = Push(b * a, ands);
else if (s[i] == '/')
ands = Push(b / a, ands);
}
}
return Pop(ands);
}
计算后缀表达式:通过后缀表达式计算就不用考虑括号问题了,直接计算即可。
- 实验结果
- 思考体会
|