表达式求值时对数据结构中栈结构的灵活应用,对于一个表达式而言,它由操作数和运算符组合而成,我们现实中常见的表达式:A+B-C,类似这种格式的我们称之为中缀表达式,但是,计算机的计算方式是有别于人的,所以,我们可以先将表达式转换为后缀表达式,再对后缀表达式进行计算,这个过程就是我们常用的表达式求解的过程。
首先,我们要解决的问题是如何将中缀表达式转换成后缀表达式。下面是相关算法:
遍历表达式,
① 从左向右依次取得字符ch并进行判断
②如果ch为操作数,则直接将该操作数拼接到后缀表达式output中(初始状态为"")。
③如果ch为运算符,则进行以下操作:
a、如果data='(',直接将'('入运算符栈。
b、如果data=')',依次弹出运算符栈中的栈顶元素,知道弹出'('为止。
c、如果不是a、b两种情况,则将ch与栈顶元素比较优先级
如果ch优先级高,或者栈为空,那么直接将ch入栈
否则,先将栈顶元素弹到output上,再将ch入栈
④如果,字符串遍历结束后,运算符栈不为空,则依次将栈顶元素弹出到output上。
实例 a+b-c
读取a,为操作数,output=a
读取+,为运算符,属于情况c,直接入栈
读取b,为操作数,output=ab
读取-,为运算符,属于情况c,将栈顶元素弹出,再将-压栈,output=ab+
读取c,为操作数,output=ab+c
遍历结束,栈不为空,依次弹出
output=ab+c-
当表达式转换成后缀表达时,计算将变得非常简单,下面是计算后缀表达式的算法:
1,设置一个栈用于存储操作数,栈初始化为空,然后从左到右扫描后缀表达式。
2,若ch为操作数,则进栈
若ch为运算符,则从栈中退出两个元素,先退出的放到运算符的左边,后弹出的放到运算符的右边,进行计算,并将运算结果压入栈中。
3,直到后缀表达式扫描结束,此时栈中只剩下一个元素,即为运算结果。
程序源码如下:
package zp.javastudy.jisuanqi;
import java.util.Stack;
public class JiSuanQi {
private String output = ""; // 后缀表达式
private Stack<Character> operasta = new Stack<>(); // 运算符栈
private Stack<Integer> numSta = new Stack<>(); // 数字栈
private String[] strs;
public static void main(String[] args) {
JiSuanQi j = new JiSuanQi();
j.change("(13%12)*33+4-54/6");
System.out.println(j.count());
// System.out.println(j.change("(22-14)*3+4-5/6"));
}
// 将中缀表达式转换为后缀表达式
public void change(String el) {
strs = el.split("[()/%+*-]"); // 使用正则表达式 将表达式中数字提取出来
int pos = 0;
// 遍历字符串
for (int i = 0; i < el.length(); i++) {
if (distinguish(el.charAt(i)) == 1) {
// 获得数字
while (strs[pos].equals("")) { // 当前分割后的字符串数组为""时,pos++
pos++;
}
output = output + strs[pos];
// 当数字为为多位数时
if (strs[pos].length() > 1) {
i = i + strs[pos].length() - 1;
}
pos++;
} else { // 字符为运算符时
if (el.charAt(i) == '(') {
operasta.push(el.charAt(i));
}
if (el.charAt(i) == ')') {
while (operasta.peek() != '(') { // 当栈顶元素为'(',终止循环
output = output + operasta.pop(); // 将栈顶元素与output进行拼接
}
operasta.pop(); // 将'('弹出操作符栈
}
if (el.charAt(i) != '(' && el.charAt(i) != ')') {
// 当字符串当前运算符优先级大于操作符栈顶元素字符串时
if (operasta.isEmpty()
|| getlevel(el.charAt(i)) > getlevel(operasta
.peek())) {
operasta.push(el.charAt(i)); // 将当前操作符入栈
} else {
output = output + operasta.pop(); // 先将栈顶元素拼接到后缀表达式中
operasta.push(el.charAt(i)); // 再将当前操作符入栈
}
}
}
}
// 遍历完字符串后,如果栈中还有运算符,则将其拼接到后缀表达式上
while (!operasta.isEmpty()) {
output = output + operasta.pop(); // 先将栈顶元素拼接到后缀表达式中
}
return;
}
// 计算后缀表达式
public int count() {
System.out.println(output);
int pos = 0;
for (int i = 0; i < output.length(); i++) {
if (distinguish(output.charAt(i)) == 1) { // 数字
// 获得数字
while (strs[pos].equals("")) { // 当前分割后的字符串数组为""时,pos++
pos++;
}
numSta.push(Integer.parseInt(strs[pos])); // 将数字入栈
// 当数字为为多位数时
if (strs[pos].length() > 1) {
i = i + strs[pos].length() - 1;
}
pos++;
} else {
if (numSta.size() == 1) {
return numSta.pop();
}
numSta.push(countdetail(numSta.pop(), numSta.pop(),
output.charAt(i)));
}
}
return numSta.pop();
}
// 具体的计算过程
public int countdetail(int b, int a, char op) { // 注意前后顺序
int ans = 0;
switch (op) {
case '+':
ans = a + b;
break;
case '-':
ans = a - b;
break;
case '*':
ans = a * b;
break;
case '/':
ans = a / b;
break;
case '%':
ans = a % b;
break;
default:
break;
}
// System.out.println(ans);
return ans;
}
// 比较优先级
public int getlevel(char ch) {
int level = 0;
switch (ch) {
case '+':
level = 1;
break;
case '-':
level = 1;
break;
case '*':
level = 2;
break;
case '/':
level = 2;
break;
case '%':
level = 2;
break;
default:
break;
}
return level;
}
// 区分数字和操作符
public int distinguish(char ch) {
// 如果为数字则返回1 否则返回2
if (ch >= '0' && ch <= '9') {
return 1;
} else {
return 2;
}
}
}