数据结构课程设计——项目3:算术表达式求解
一、问题描述和项目要求
1.1 问题描述
设计一个简单的算术表达式计算器。
1.2 基本要求
1)实现标准整数类型的四则运算表达式的求值(包含括号,可多层嵌入)。
2)要求自行设计非法表达式,进行程序测试,以保证程序的稳定运行。
二、问题分析
2.1 程序功能设计分析
1)算数表达式中有效字符有:0~9、+、-、*、/、(、)。
2)运算符的优先级为乘=除>加=减,同优先级左大于右,
3)应检查算术表达式左右括号是否对应匹配。
4)应注意检查表达式中是否有非法字符,应注意0不能作为除数。
5)程序可忽略中缀表达式中的所有空格,系统认为4 8+5为48+5,48 + 5 =为48+5=。
2.2 程序实现分析
1. 输入的算数表达式例如 (30 + 2 * 70 )/ 3 - 12 * 3 是一个中缀表达式,即运算符在中间,操作数在运算符的两边,例中的2*70,*为运算符,2和70分别为左右操作数。对于中缀表达式,我们人能直观地看出算数表达式的含义进行求值,而计算机程序却难以理解中序表达式的含义。
2. 而利用栈的先进先出结构,计算机能很好的处理后缀表达式。 例如 30 2 70 * + 3 / 12 3 * - 就是一个后缀表达式,后缀表达式两操作数在前,运算符在后,后续表达式不需要括号利用栈也能清楚地知道运算顺序。 如 12 3 *,12 和 3 为两个操作数,* 为运算符 。
3. 因此对于输入的中缀表达式,只需将其转为后缀表达式,再利用栈就能求解得值。
三、逻辑设计
3.1 抽象数据类型的定义
程序整体的抽象数据类型定义
ADT Calculation
Data:
中缀表达式
后缀表达式
运算符栈
操作数栈
Operation:
isNumber
输入: 字符c
输出: 无
返回: 返回传入的字符c是否为数字0~9
isOperation
输入: 字符c
输出: 无
返回: 返回传入的字符c是否为操作符
isLeftBrackets
输入: 字符c
输出: 无
返回: 返回传入的字符c是否为左括号(中英文左括号均可)
isRightBrackets
输入: 字符c
输出: 无
返回: 返回传入的字符c是否为右括号(中英文左括号均可)
comparator// 比较两运算符优先级
输入: ch1栈顶运算符,ch2遍历到的运算符
输出: 无
返回: 返回1表示ch1>ch2,返回-1表示ch1<ch2,返回0表示ch1=ch2
translate// 将中缀表达式转换为后缀表达式
输入: 无
输出: 无
返回: 返回转换是否成功,若表达式不合规范返回false
calculate// 利用后缀表达式计算算数表达式
输入: 无
输出: 计算结果
返回: 返回计算结果
check// 初步检查算数表达式是否符合规范
输入: 无
输出: 无
返回: 返回true表示算数表达式符合规范,反之不符合规范
endADT
3.2 程序流程图
四、主要方法的算法设计
4.1 translate方法
-
作用:将输入的中缀表达式转换未后缀表达式
-
算法思路(这里仅对于一个符合规范的中缀表达式,要求中缀表达式末尾以=结束):
1) 初始化一个存放操作符的栈,将一个‘=’入栈(这里可不放入,放入是方便后面的处理),初始化后缀表达式为空字符串,初始化count=0用于记录扫描到的左括号数
2) 顺序读取中缀表达式
2.1 若读取到的为操作数,直接加入后缀表达式末尾。
2.2 若读取到的为操作符记为ch2,若栈大小为1,即栈中只有起始的=,ch2直接入栈,若栈大小大于1,即栈中有除了起始的=以外其余的字符,记栈顶元素为ch1,比较ch1与ch2的优先级,优先级为左括号>乘=除>加=减>右括号,同优先级的中缀表达式中左边的>右边的(也即栈中的大于新扫描到的),特别处理左括号优先级与右括号相同。
2.2.1 若ch1<ch2,则ch2进栈,继续顺序读取中缀表达式。
2.2.2 若ch1>ch2,则栈顶元素ch1出栈加入后缀表达式末尾,继续比较新的栈顶元素ch1’ 与ch2优先级,遵循2.2.1和2.2.2的处理过程。
2.3 若读取到的为左括号,直接入栈(按照优先级比较结果也是这样,这里可简化比较优先级的过程),count++。
2.4 若读取到的为右括号,如果count<1,则右括号没有匹配到左括号,返回false表示括号匹配失败;如果count>=1(该右括号匹配到了1个左括号),count–,栈顶元素持续出栈并加入后缀表达式末尾直到栈顶元素为左括号, 将左括号出栈但不加入后缀表达式,继续顺序读取中缀表达式。
2.5 若读取到的为**=**,说明顺序表达式已读取完毕,若此时count>0,说明还有左括号仍未被匹配,返回false表示括号匹配失败。若count==0,说明所有括号都匹配成功,**将栈顶元素持续出栈并加入后缀表达式末尾直到栈中只有一个元素。**将=号也出栈,后缀表达式以转换完毕。最后返回true表示所有括号均已匹配成功。 -
方法源码如下:
// 用到的变量如下:
String inOrder;// 中缀表达式
String postOrder;// 后缀表达式
// 运算符栈
Stack<Character> opreationStack = new Stack<Character>();
/**
* 将中缀表达式转换为后缀表达式
* @return 返回转换是否成功, 只能检测所有括号是否对应匹配
*/
boolean translation() {
opreationStack.push('=');
String s = "";
int count = 0;// 记录操作符栈中左括号个数
for (int i = 0; i < inOrder.length(); i++) {
char ch2 = inOrder.charAt(i);
if (ch2 == ' ') {
continue;
} else if (isNumber(ch2)) {
s = s.concat(String.valueOf(ch2));
} else if (isOperation(ch2)) {
if (s.length() > 0) {
postOrder = postOrder.concat(s + " ");
s = "";
}
int c;
do {
char ch1 = opreationStack.peek();
c = comparator(ch1, ch2);
if (c == 1) {
postOrder = postOrder.concat(String.valueOf(ch1) + " ");
opreationStack.pop();
}
} while (opreationStack.size() > 1 && c == 1);
// 栈空或ch1<ch2
opreationStack.push(ch2);
} else if (isLeftBrackets(ch2)) {
// 左操作符直接入栈
opreationStack.push(ch2);
count++;
} else if (isRightBrackets(ch2)) {
if (count < 1) {
// 没匹配到左括号
System.out.println("括号未匹配");
return false;
} else {
// 能匹配到左括号
if (s.length() > 0) {
postOrder = postOrder.concat(s + " ");
s = "";
}
int c;
do {
char ch1 = opreationStack.peek();
c = comparator(ch1, ch2);
if (c != 0) {
// 一直循环遇到左括号为止
postOrder = postOrder.concat(String.valueOf(ch1) + " ");
/* System.out.println(opreationStack.pop());*/
opreationStack.pop();
}
} while (opreationStack.size() > 1 && c != 0);
opreationStack.pop();
count--;
}
} else if (ch2 == '=') {
if (count > 0) {
System.out.println("括号未匹配");
return false;
}
if (s.length() > 0) {
postOrder = postOrder.concat(s + " ");
s = "";
}
while (opreationStack.size() > 1) {
char ch1 = opreationStack.peek();
postOrder = postOrder.concat(String.valueOf(ch1) + " ");
opreationStack.pop();
}
opreationStack.pop();
}
}
return