*文中内容来源于《数据结构 --Java语言描述》(第二版) 刘小晶 杜选 主编
*此系列文章作为学习记录,若文中内容有误,请大家指出,谢谢
算术表达式
算术表达式是由操作数、算术运算符和分隔符所组成的句子。表达式一般由中缀表达式、后缀表达式和前缀表达式三种表现形式。。
其中,中缀表达式是将运算符放在两个操作数中间,这正是平时人们平时书写算术表达式的一种描述方式;**后缀表达式(也称逆波兰表达式)**是将运算符放在两个操作符之后,而前缀表达式是将运算符放在两个操作数之前。
由于运算符有优先级,所以在计算机内部是使用中缀表达式描述时,对计算是非常不方便的,尤其带括号时。而后缀表达式中既无运算符优先级又无括号的约束问题,因为后缀表达式中运算符出现的顺序正是计算的顺序,所以计算一个后缀表达式的值比计算一个中缀表达式的值要简单得多。
实现算法的基本思想如下:
(1) 初始化一个运算符栈;
(2) 从算术表达式输入的字符串中从左到右读取一个字符;
(3) 若当前字符是操作数,则直接送往后缀表达式;
(4) 若当前字符是左括号”(“时,将其压进运算符栈;
(5) 若当前字符为运算符是,则:
a. 当运算符栈为空,将其压入运算符栈;
b. 当此运算符的优先数高于栈顶运算符,则将此运算符压入运算符栈;否则,弹出栈顶运算符送往后缀式,并将当前运算符压栈,重复步骤(5);
(6) 若当前字符是右括号”)”时,反复讲栈顶符号弹出,并送往后缀表达式,直到栈顶符号为左括号为止,再将左括号出栈并丢弃;
(7) 若读取还为完成,则跳转到(2);
(8) 若读取完毕,则将栈中剩余的所有运算符弹出并送往后缀表达式。
public class Example3_3 {
public String convertToPostfix(String str) throws Exception{
LinkStack ls = new LinkStack(); //创建一个新的链栈用来存储括号和运算符
String postfix = new String(); //创建一个新的字符串用来存储后缀表达式
for (int i = 0; (i < str.length() && str != null); i++) { //每一个字符进行判断
char item = str.charAt(i);
if (item != ' ') {
if (isOpenParenthesis(item)) { //将左括号压入栈中
ls.push(item);
} else if (isCloseParenthesis(item)) { //遇到右括号,将栈中的运算符加到后缀表达式中,然后弹出左括号
char top = (Character) ls.pop();
while (!isOpenParenthesis(top)) {
postfix = postfix.concat(String.valueOf(top));
top = (Character) ls.pop();
}
} else if (isOperator(item)) {
if (!ls.isEmpty()) {
char top = (Character) ls.pop();
while (top != ' ' && (priority(item) <= priority(top))) {
postfix = postfix.concat(String.valueOf(top));
if (!ls.isEmpty()) {
top = (Character) ls.pop();
}
else
top = ' ';
}
if (top != ' ')
ls.push(top);
}
ls.push(item);
} else {
postfix = postfix.concat(String.valueOf(item));
}
}
}
for (int i = 0; i <= ls.length(); i++){
postfix = postfix.concat(String.valueOf(ls.pop()));
}
return postfix;
}
//对后缀表达式进行求值计算的函数
public double numberCalculate(String postfix) throws Exception{
LinkStack st = new LinkStack();
for (int i = 0; postfix != null && i < postfix.length(); i++){
char c = postfix.charAt(i); //从后缀表达式中读取一个字符
if (isOperator(c)){ //当为操作数时
//取出两个操作数
double d2 = Double.valueOf(st.pop().toString());
double d1 = Double.valueOf(st.pop().toString());
double d3 = 0;
if ('+' == c){
d3 = d1 + d2; //加法运算
}
else if ('-' == c){ //减法运算
d3 = d1 - d2;
}
else if ('*' == c){ //乘法运算
d3 = d1 * d2;
}
else if ('/' == c){ //除法运算
d3 = d1 / d2;
}
else if ('^' == c){ //幂运算
d3 = Math.pow(d1, d2);
}
else if ('%' == c){
d3 = d1 % d2;
}
st.push(d3);
}
else { //当为操作数时
st.push(c);
}
}
return (Double)st.pop(); //返回运算结果
}
//判断字符串是否为运算符
public boolean isOperator(char c){
if ('+' == c || '-' == c || '*' == c || '/' == c || '^' == c || '%' == c){
return true;
}
else{
return false;
}
}
//判断字符串是否为开括号
public boolean isOpenParenthesis(char c){
return '(' == c;
}
//判断字符串是否为闭括号
public boolean isCloseParenthesis(char c){
return ')' == c;
}
//判断运算法的优先级
public int priority(char c){
if (c == '^'){ //为幂运算
return 3;
}
if (c == '*' || c == '/' || c == '%'){ //为乘、除、取模运算
return 2;
}
else if (c == '+' || c == '-'){ //为加、减运算
return 1;
}
else{ //其他
return 0;
}
}
public static void main(String[] args) throws Exception{
Example3_3 p = new Example3_3();
String postfix = p.convertToPostfix("(1+2)*(5-2)/2^2+5%3"); //转化为后缀表达式
System.out.println("后缀表达式为:" + postfix);
System.out.println("表达式结果为:" + p.numberCalculate(postfix)); //对后缀表达式求值后,并输出
}
}
关于链栈的代码我放在了另一篇博客中:顺序栈与链栈