逆波兰表达式又叫做后缀表达式
,它将复杂表达式转换为可以依靠简单的操作得到计算结果的表达式,解决了四则运算中括号改变运算符优先级的问题。
四则运算的表达式一般都是中缀表达式,如1 + 2 * (3 - 4) + 5
,即操作符在两个操作数之间。四则运算需要两个步骤,一是把中缀表达式转为后缀表达式,二是由后缀表达生成结果中缀表达式转为后缀表达式算法描述。
中缀到后缀的变换过程
1. 所用数据结构:首先有个包含中缀表达式元素列表sourceList
,然后创建一个符号列表destList
保存最终后缀表达式,创建一个操作符堆栈opStack
(作为由sourceList
转为destList
的中介)。
2. 从sourceList
取出一个元素A
,如果是数字则加入到destList
中;如果是运算符,将操作符A
与操作符堆栈opStack
栈顶的运算符的优先关系相比较。如果优先关系高于opStack
栈顶的运算符,则将该运算符压入操作符堆opStack
;倘若不是(低于或等于)的话,则将运算符栈opStack
栈顶的运算符从栈中弹出保存到destList
,重复此步骤,直到操作符A
压入操作符堆栈opStack
(对于+
、-
来说,把opStack
全部弹出,然后把A
压入opStack
中)。如果是左括号(
,则压入操作符堆栈opStack
(注意,(
将之后的运算符的优先级与之前opStack
中运算符的优先级隔开)。如果是右括号)
,则操作符堆栈opStack
弹出操作符并加入到destList
中,直到弹出左括号(
。
3. 重复步骤2
中的操作,所有元素处理完毕后将操作符堆栈opStack
弹出操作符并加入到destList
中,这样中缀式表示的简单算术表达式转化为逆波兰表达式所表示的简单算术表式。
中缀表达式例如1 + 2 * (3 - 4) + 5
,构造元素列表1 + 2 * ( 3 - 4 ) + 5
,构造一个空最终后缀表达式destList
,一个操作符堆栈opStack
。
- 取出
1
,destList[ 1 ]
,opStack[ ]
- 取出
+
,destList[ 1 ]
,opStack[ + ]
- 取出
2
,destList[ 1 2 ]
,opStack[ + ]
- 取出
*
,destList[ 1 2 ]
,opStack[ + * ]
- 取出
(
,destList[ 1 2 ]
,opStack[ + * ( ]
- 取出
3
,destList[ 1 2 3 ]
,opStack[ + * ( ]
- 取出
-
,destList[ 1 2 3 ]
,opStack[ + * ( - ]
- 取出
4
,destList[ 1 2 3 4 ]
,opStack[ + * ( - ]
- 取出
)
,destList[ 1 2 3 4 - ]
,opStack[ + * ]
:操作符堆栈opStack
弹出操作符并,加入到destList
中,直到弹出左括号(
。 - 取出
+
,destList[ 1 2 3 4 - * + ]
,opStack[ + ]
:加号优先级不大于[ + * ]
。 - 取出
5
,destList[ 1 2 3 4 - * + 5 ]
,opStack[ + ]
- 处理完毕,destList
[ 1 2 3 4 - * + 5 + ]
,opStack[ ]
用后缀表达式来计算结果
遍历储存后缀表达式的列表,将元素依次进栈,当遇到操作符时,连续出栈两个元素,进行运算,再将结果进栈,最后栈内留下的元素就是计算结果。
后缀表达式destList [ 1 2 3 4 - * + 5 + ]
,结果堆栈resultStatck [ ]
格式为输入 -> 结果
:
[ 1 2 3 4 ] -> resultStatck [ 1 2 3 4 ]
[ - ] -> resultStatck [ 1 2 3-4 ]
[ * ] -> resultStatck [ 1 2*(3-4) ]
[ + ] -> resultStatck [ 1+2*(3-4) ]
[ 5 ] -> resultStatck [ 1+2*(3-4) 5 ]
[ + ] -> resultStatck [ 1+2*(3-4)+5 ]
要实现一个简单的计算器,可以对+ - * / ( )
进行处理并返回正确的值,最先想到的就是逆波兰表达式。首先,用到第一个栈把算式转化为逆波兰表达式存一个数组中,在使用另一个栈对这个数组进行判断、出栈、运算、入栈、判断…,不断处理,最后的栈顶元素就是结果。
#include <iostream>
#include <stack>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
/* 把一个算式先转化为逆波兰表达式 */
int Priority ( char ch ) { /* 定义优先级别 */
int i;
switch ( ch ) {
case '(': i = 1; break;
case '+': i = 2; break;
case '-': i = 2; break;
case '*': i = 4; break;
case '/': i = 4; break;
case ')': i = 5; break;
default : i = -1; break;
}
return i;
}
void tonibolan ( char *ch, char retch[100] ) {
stack<char> st2;
int i = 0;
while ( *ch != '\0' ) {
if ( *ch >= '0' && *ch <= '9' ) {
retch[i++] = *ch;
} else if ( *ch == '(' ) { /* 左括号直接压栈 */
st2.push ( *ch );
} else if ( *ch == ')' ) {
while ( st2.top() != '(' ) {
retch[i++] = st2.top();
st2.pop();
}
if ( st2.top() == '(' ) {
st2.pop();
}
} else if ( st2.empty() || Priority ( *ch ) > Priority ( st2.top() ) ) {
st2.push ( *ch );
} else {
while ( Priority ( *ch ) <= Priority ( st2.top() ) ) {
retch[i++] = st2.top();
st2.pop();
if ( st2.empty() ) {
break;
}
}
st2.push ( *ch );
}
ch++;
}
while ( !st2.empty() ) {
retch[i++] = st2.top();
st2.pop();
}
}
/* 计算逆波兰表达式的值 */
int calcval ( char *ret ) {
stack<char> st;
while ( *ret != '\0' ) {
if ( *ret >= '0' && *ret <= '9' ) {
st.push ( *ret );
} else {
switch ( *ret ) {
case'+': {
char a = st.top();
st.pop();
char b = st.top();
st.pop();
st.push ( ( ( a - '0' ) + ( b - '0' ) + '0' ) );
break;
}
case'-': {
char a = st.top();
st.pop();
char b = st.top();
st.pop();
st.push ( ( ( b - '0' ) - ( a - '0' ) ) + '0' );
break;
}
case'*': {
char a = st.top();
st.pop();
char b = st.top();
st.pop();
st.push ( ( ( b - '0' ) * ( a - '0' ) ) + '0' );
break;
}
case'/': {
char a = st.top();
st.pop();
char b = st.top();
st.pop();
if ( a != '0' ) {
st.push ( ( ( ( b - '0' ) / ( a - '0' ) ) + '0' ) );
} else {
cout << "除数为0错误" << endl;
}
break;
}
}
}
ret++;
}
return st.top() - '0';
}
int main() {
char ret[100] = {0};
char ch[100] = {0};
cin.get ( ch, 100 );
tonibolan ( ch, ret );
int len = sizeof ( ret ) / sizeof ( 0 );
int i = 0;
cout << "算式的逆波兰表达式为:" << endl;
while ( len-- ) {
cout << ' ' << ret[i++];
}
cout << "\n算式的计算结果为:" << endl;
cout << calcval ( ret ) << endl;
return 0;
}