日常生活中,我们所遇见的数学表达式都是类似于a+b-xy这样的中缀表达式。但在计算机中,是如何翻译这个表达式并对其求值呢?这里引入一个名词波兰表示,也叫做逆波兰记号、PNP,是一种计算机中无括号的后缀表达式,是中缀表达式的替代形式,参加运算的操作数总在操作符前面。例如,x+y*(a-b)-d/e所对应的后缀表达式是xyab-*+de/。计算机运用该方法进行计算的原理在于,从左向右扫描时,当遇到操作数,则将操作数进栈;当遇到操作符时,弹出两个操作数,进行运算后,将新的结果压入栈中。循环如此,直到栈内不含操作符,弹出结果。
该算法的特点在于,操作数与操作符使用同一个栈,但是要将算术表达式先转换为后缀表达式。
受此启发,我运用两个栈,一个是存放操作数的data栈,一个是存放操作符op栈。利用事先定义好的运算符优先级,将操作符压栈或退栈。
运算符优先级如下:
- 栈内操作符(instack)优先级:#=0;(=1;*=5;/=5;%=5;+=3;-=3
- 栈外操作符(outstack)优先级:#=0;(=6;*=4;/=4;%=4;+=2;-=2
算法步骤:
1.操作符站初始化,将#压入op栈内,从表达式中读取字符ch。
2.若ch是操作数,则直接压入data栈内。
3.若ch是操作符,则判断instack(op)与outstack(ch)的优先级
- 若instack(op)>=outstack(ch),弹出op栈的栈顶元素,并弹出data栈的两个元素,进行运算后,将结果压入data栈内。再比较op栈的栈顶元素与ch的优先级大小。
- 若instack(op)<outstack(ch),将ch压入op栈内。
4 算法结束
注意:
若是用字符数组来容纳表达式,则需要注意处理多位数的情况。
办法很简单,判断当前单元是否为数字,若是,则计数变量count加一,再接着判断它的下一个单元,若下一个单元仍然是数字,count加一;若不是,则将count个数字组合成所需要的数值,代替count个数字压入栈内成为一个数字。
代码实现:
//栈内优先级定义
template <class T>
int Seqstack<T>::instack(char c)
{
int x;
switch (c)
{
case '#':x = 0; break;
case '(':x = 1; break;
case '*':
case '/':
case '%':x = 5; break;
case '+':
case '-':x = 3; break;
case ')':x = 6; break;
}
return x;
}
//栈外优先级定义
template <class T>
int Seqstack<T>::outstack(char c)
{
int z;
switch (c)
{
case '#':z = 0; break;
case '(':z = 6; break;
case '*':
case '/':
case '%':z = 4; break;
case '+':
case '-':z = 2; break;
case ')':z = 1; break;
}
return z;
}
//关键算法
template <class T>
void Seqstack<T>::calculate()
{
Seqstack<char> s;
Seqstack<double>temp;
char ch = '#', ch1, op;
double left, right,val, value;
int i = 0, count = 0;
double re = 0;
s.Push(ch);
char exp[100];
cout << "请输入算术表达式(并以#号结束):";
cin >> exp;
while (s.IsEmpty() == false ) /*
{
if (isdigit(exp[i]))
{
temp.Push(exp[i] - '0');
count += 1;
if (!isdigit(exp[i + 1]))
{
for (int k = 0; k < count; k++) 这一块是处理多位数的数字
{
temp.Pop(val);
re += val*pow(10, k);
}
temp.Push(re);
}
} */
else
{
re = 0;
count = 0;
s.Peek(ch1);
while (instack(ch1) > outstack(exp[i]))
{
temp.Pop(right);
temp.Pop(left);
s.Pop(op);
switch (op)
{
case '+':value = left + right; temp.Push(value); break;
case '-':value = left - right; temp.Push(value); break;
case '*':value = left * right; temp.Push(value); break;
case '/':
if (right == 0.0)
{
cout << "Divide by 0!" << endl;
MakeEmpty();
}
else{ value = left / right; temp.Push(value); }
break;
}
s.Peek(ch1);
}
if (instack(ch1) < outstack(exp[i]))
{
s.Push(exp[i]);
}
else
{
s.Pop(op);
}
}
i++;
}
cout << temp <<s<< endl;
temp.MakeEmpty();
s.MakeEmpty();
}
程序结果: