Java实现利用栈进行表达式求值(有小数运算和取余、次方等运算)
1.需求分析
在学习完栈的基本知识之后,我们理解了栈作为一种非常重要的数据结构的重要性和独特性,它具有很多突出且优异的性质,这让我们能够用它来完成很多工作和比较困难的问题。在C语言版的《数据结构》教材中有一个运用栈来进行表达式求值的模块功能,但是它所要求的功能仅有加减乘除四则运算并且只支持int型的变量。在这个算法的基础上实验二就要求我们再设计一个运用栈来实现表达式求值的程序。在这个程序中我们需要实现:
“设计一个程序,实现用算符优先法对算术表达式求值的过程。对本设计系统实现+、-、、/、%和乘方(^)运算。符合要求,同时提高自己的编程能力。实现算术表达式求值。
选作内容:提供对小数点的支持,允许输入的表达式中出现多位数字和小数点。“
(摘自郁松老师的实验指导书)
所以我计划完成以下任务:
输入的形式:算符表达式
输入值的范围:-127—128(包含任意的小数) +、-、、/、%和乘方(^)
输出的形式:float型数据(考虑到float型可表示小数并且计算比较快)
测试数据:3*(13.5-10)-4%3-8/2-4^2
结果为-10.5
利用栈的基本操作(初始化栈、判栈为空、出栈、入栈等运算),设计一个程序,实现用算符优先法对算术表达式求值(实现+、-、*、/、%和乘方(^)运算)的过程。
2.概要设计
为了实现算符优先级的算法,我根据书上的例子,使用了两个工作栈,分别为OPTR,用以寄存运算符、OPND,用于寄存操作数和运算结果。标识符“#”置于栈底,来作为程序出口的标志。在整个程序中包含三个Class,它们分别是进行push、pop、取得栈顶元素等栈操作的ArrayStack类和负责读取输入串、进行判断优先级、递归、计算子表达式、利用两个栈进行整体计算的EvaluateExpression类以及通过键入表达式进行测试的类Test01。在Test01中我新建了两个ArrayStack对象用来表示OPND和OPTR栈,并且new了一个EvaluateExpression对象来使用这两个栈并且计算结果。最后将计算结果通过控制台输出。
3.详细设计
首先我们置操作数栈为空栈,将标识符“#”设置为运算符的栈底元素。我们通过控制台输入一个输入串,我们依次读入表达式中的每个字符。设置c为当前读取到的字符,index为读取字符串的指针。栈顶指针设置为topPointer。输入的输入串为expression。设置一个flag变量用来表示读取的数字的前一为是否为数字(以此来识别多位数字)。然后开始进行读取操作。
如果是操作数就进入OPND栈,然后利用charAt()函数读取下一个字符,并且置flag为1。如果下一个还是数字的话就将OPND栈的栈顶元素pop出来并且将它乘以10之后与新得到的数相加,之后将其再压栈,再遇到数字依次类推。如果遇到小数点(实现小数操作),那么就接着读取下一位数字,接着进入while循环,设置一个变量i用来记录循环的次数。这时得到的数字值就是其本身的值再乘以10^(-i),接着把这个结果加到栈顶元素的值上,将新的数字压栈,与上面类似。这样我们就完成了数字的识别工作。
如果是运算符就和OPTR栈中的栈顶运算符比较优先权后再做相应的操作。如果新得到的运算符优先权高就进行压栈操作,优先权相等就表示运算式计算完毕。优先权低就要取出数字栈顶的两个元素进行运算。然后再将计算结果压栈。最终当“#”相遇时运算结束,返回数字栈顶的值即为运算结果。优先权的表示我是通过构造一个二维数组来进行实现的。首先我把九个操作符+、-、*、/、% 、^、(、)、#分别标号为0到8,然后构造了一个二维数组来表示他们之间的优先级关系。
0表示高于,1表示低于,2表示等于,3表示出错。
0 0 1 1 1 1 1 0 1
0 0 1 1 1 1 1 0 0
0 0 0 0 0 1 1 0 0
0 0 0 0 0 1 1 0 0
0 0 0 0 0 1 1 0 0
0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 2 3
1 1 1 1 1 1 3 1 0
1 1 1 1 1 1 1 3 2
4.调试分析
1、 在编写代码的过程中我一时没有想到如何在计算整体的同时计算括号内的内容,经过网上的资料查阅我发现这个可以通过使用一个递归来实现,最后通过递归方法我完成了这个功能。
2、 在完成附加功能的过程中,我遇到了不少的困难。首先如何将小数点后的数字表示的数值准确地加入到整体数字中。经过思考我发现可以设置一个变量i来进行循环次数的记录以此来完成准确表示表达式中的数字的值的功能。
3、 在整体框架写完进行第一次的测试过程中,我的程序并不能跑得动并且陷入了死循环,这让我感到很奇怪,最后经过debug和排查后发现是ArrayStack中的pop()函数中忘记让栈顶指针自减了。
4、 因为我的很多功能都是自己手写的,所以很多细节就会出现一些小问题,比如适时地进行charAt就很重要,我经过debug和检查循环等结构最终将整体的问题都解决完。最终实现了结果的正确输出。
代码:
public class Test01 {
public static void main(String[] args) throws Exception {
ArrayStack OPND = new ArrayStack(999);
ArrayStack OPTR = new ArrayStack(999);
EvaluateExpression evaluateExpression = new EvaluateExpression();
float sum = evaluateExpression.EvaluateExpression(OPTR,OPND);
System.out.println("The answer is "+sum);
}
}
import java.util.Arrays;
public class ArrayStack<T> {
private int max_size;
private T[] array;
private int topPointer;
public ArrayStack(int max_size) {
this.max_size = max_size;
this.array = (T[]) new Object[max_size];
this.topPointer = -1;
}
public void push(T t) throws Exception{
topPointer++;
if (topPointer<=array.length-1){
array[topPointer] = t;
}else {
throw new Exception("Sorry sir,The stack is full so you can't put things anymore!");
}
}
public T pop() throws Exception{
if (topPointer>=0){
return array[topPointer--];//should return the top data
}else {
throw new Exception("Sorry sir, the stack can't be pop anymore!");
}
}
@Override
public String toString() {
return "ArrayStack{" +
"array=" + Arrays.toString(array) +
'}';
}
public T getTop() throws Exception{
if (topPointer<0) throw new Exception("The Stack is empty!");
else return array[topPointer];
}
public boolean isEmpty(){
if (topPointer<0) return true;
else return false;
}
}
import java.util.ArrayList;
import java.util.Scanner;
public class EvaluateExpression {
private static String expression;//expression 表示表达式
private static int index = 0;//使用静态的话可以用类名直接访问
private static char c;//使用c来保存读到的字符
static {
Scanner scanner = new Scanner(System.in);
System.out.println("Please enter the expression:");
expression = scanner.nextLine();
expression += "#";
c = expression.charAt(index);
}
public boolean isDigit(char c) {
if (c >= '0' && c <= '9') {
return true;
} else return false;
}
public int change_char_into_int(char c) {
switch (c) {
case '+':
return 0;
case '-':
return 1;
case '*':
return 2;
case '/':
return 3;
case '%':
return 4;
case '^':
return 5;
case '(':
return 6;
case ')':
return 7;
case '#':
return 8;
default:
return -1;
}
}
public char compare(char a, char b) {
int[][] symbol = {
{0, 0, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 2, 3},
{1, 1, 1, 1, 1, 1, 3, 1, 0},
{1, 1, 1, 1, 1, 1, 1, 3, 2},
};
int i, j, flag;
i = change_char_into_int(a);
j = change_char_into_int(b);
flag = symbol[i][j];
switch (flag) {
case 0:
return '>';
case 1:
return '<';
case 2:
return '=';
default:
return 3;
}
}
public float Operate(float a, char x, float b) {
float temp = 0;
switch (x) {
case '+': {
temp = a + b;
break;
}
case '-': {
temp = a - b;
break;
}
case '*': {
temp = a * b;
break;
}
case '/': {
temp = a / b;
break;
}
case '%': {
temp = a % b;
break;
}
case '^': {
temp = (float) Math.pow(a, b);
break;
}
}
return temp;
}
public float EvaluateExpression(ArrayStack OPTR, ArrayStack OPND) throws Exception {
OPTR.push('#');
int num = 0;//It is used to store the number got before
char x;
int flag = 0;//It is used to judge the thing get before is number or not(if is 1 true
while (c != '#' || (char) OPTR.getTop() != '#') {
if (c == ')' && (char) OPTR.getTop() == '#') {
c = expression.charAt(++index);
return (float) OPND.getTop();
}
if (c == '(') {
c = expression.charAt(++index);
float sum = EvaluateExpression(new ArrayStack(99), new ArrayStack(99));
OPND.push(sum);
}
if (isDigit(c)) {
switch (flag) {
case 0:
OPND.push((float) c - '0');
c = expression.charAt(++index);
flag = 1;
break;
case 1:
float tens = (float) OPND.getTop();
float temp = tens * 10 + (float) (c - '0');
OPND.pop();
OPND.push(temp);
flag = 1;
c = expression.charAt(++index);
}
if (c == '.') {
int i = 1;
c = expression.charAt(++index);
while (isDigit(c)) {
float temp = (float) OPND.getTop();
float temp2 = (temp + (c - '0') * (float) Math.pow(10, (-i)));
OPND.pop();
OPND.push(temp2);
c = expression.charAt(++index);
i++;
}
flag = 0;
}
} else {
flag = 0;
switch (compare((char) OPTR.getTop(), c)) {
case '<': {
OPTR.push(c);
c = expression.charAt(++index);
break;
}
case '=': {
OPTR.pop();
c = expression.charAt(++index);
break;
}
case '>': {
x = (char) OPTR.pop();
float a = (float) OPND.pop();
float b = (float) OPND.pop();
OPND.push(Operate(b, x, a));
break;
}
}
}
}
return (float) OPND.getTop();
}
}