目录
一、需求一
A:要求输入的是中缀表达式,如1+2*3-10/5;
B:要求输入运算符只有加、减、乘、除;
C:返回中缀表达式的结果;
-
思路分析
A:定义两个栈对象,一个用于存放表达式中的数据,一个用于存放表达式中的符号;
B:中缀表达式以字符串的形式给出,利用循环对字符串进行扫描得到每一个字符;
a:该字符为符号,则判断符号栈是否为空;
是:直接入栈;
否:判断当前字符的优先级与符号栈栈顶元素的优先级;
a:若当前字符的优先级更高,那么,将该字符入栈;
b:若当前字符的优先级低于或等于符号栈栈顶元素,那么就将从存放数据的栈中
弹出两个元素,弹出符号栈栈顶元素,做运算,结果在入数据栈;
b:该字符不是符号,判断该字符是否为字符串最后一个元素;
是:将该字符转换为Integer类型后压入堆栈;
否:判断该字符的下一个字符是否为符号?
是:将该字符转换为Integer类型后压入堆栈;
否:不搭理
C: 字符串扫描完毕后,利用循环,每次数栈中出栈两个元素,符号栈中出栈一个符号,
当符号栈为空的时候,就退出循环,将数栈中的值pop出;
-
代码实现
package cn.itcast_01;
public class Calculator {
public static void main(String[] args) {
//定义一个字符串表达式
String expression = "1-2*3+8/4";
//创建两个栈
ArrayStack2 numStack = new ArrayStack2(10);
ArrayStack2 operStack = new ArrayStack2(10);
//定义相关变量
int index = 0;
int num1 = 0;
int num2 =0;
int oper = 0;
int res = 0;
char ch = ' ';//保存扫描的char
String keepNum = "";//用于拼接多位数
//使用循环来扫描字符串
while (true) {
// 获取字符
ch = expression.substring(index, index + 1).charAt(0);
// 判断ch是什么
if (operStack.isOper(ch)) {
// 判断当前符号栈是否为空
if (!operStack.isEmpty()) {
// 不为空
while (!operStack.isEmpty() && operStack.priority(ch) <= operStack.priority(operStack.peek())) {
if (numStack.Length() >= 1)
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.cal(num1, num2, oper);
// 把运算结果入数栈
numStack.push(res);
}
// 当前操作符优先级大于栈内操作符优先级
operStack.push(ch);
} else {
// 为空
operStack.push(ch);
}
} else {
// 1.数字是多位数的情况
keepNum += ch;
// 判断ch是否为字符串最后一个元素
if (index == expression.length() - 1) {
numStack.push(Integer.parseInt(keepNum));
} else {
// 判断下一个字符是不是符号
if (operStack.isOper(expression.substring(index + 1, index + 2).charAt(0))) {
numStack.push(Integer.parseInt(keepNum));
// 重要的地方,清空keepNum
keepNum = "";
}
}
}
index++;
if(index >= expression.length()) {
break;
}
}
//表达式扫描完毕后,进行运算
while(true) {
//如果符号栈为空,则计算结束,数栈中只有1个值
if(operStack.isEmpty()) {
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.cal(num1, num2, oper);
numStack.push(res);
}
System.out.printf("表达式的结果%s = %d",expression,numStack.pop());
}
}
//先创建一个栈,需要扩展功能
class ArrayStack2 {
// 列写栈的成员变量
private int top = -1;
private int[] array;
private int maxSize;
// 给出栈的带参构造方法
// ArrayStack2(int[] array, int maxSize) {
// array = new int[maxSize];
// }
ArrayStack2(int MaxSize) {
this.maxSize = MaxSize;
//初始化数组
array = new int[this.maxSize];
}
//返回栈中有效元素的个数
public int Length() {
return top+1;
}
//返回当前栈顶的元素,不是出栈
public int peek() {
return array[top];
}
//判断栈满方法
public boolean isFull() {
return top == maxSize - 1;
}
//判断栈空方法
public boolean isEmpty() {
return top == -1;
}
// 定义入栈方法
public void push(int value) {
// 判断是否栈满
if (isFull()) {
System.out.println("栈满");
return;
} else {
array[++top] = value;
}
}
// 定义出栈方法
public int pop() {
// 判断是否栈空
int value = 0;
if (isEmpty()) {
//抛出异常
throw new RuntimeException("栈空");
} else {
value = array[top--];
}
return value;
}
//栈的遍历
public void list() {
//判断栈是否为空
if (isEmpty()) {
System.out.println("栈空");
return;
} else {
for(int i=top;i>=0;i--) {
System.out.print(array[i]+" ");
}
}
}
//返回运算符的优先级,优先级是程序员来确定的,优先级使用数字
//表示,数字越大,则优先级越高
public int priority(int oper) {
if(oper == '*'|| oper == '/') {
return 1;
} else if(oper == '+'|| oper == '-') {
return 0;
} else {
return -1;
}
}
//判断是不是一个运算符
public boolean isOper(char val) {
return val == '+'|| val == '-' ||
val == '*' || val == '/';
}
//运算方法
public int cal(int num1,int num2,int oper) {
int res = 0;
switch(oper) {
case '+':
res = num1+num2;
break;
case '-':
res = num2-num1;//注意顺序
break;
case '*':
res = num1*num2;
break;
case '/':
res = num2/num1;
break;
default:break;
}
return res;
}
}
二、需求二
A:扩展运算符,加入小括号;
-
思路分析
A:在上面代码的基础上加入小括号,遇到左括号要入符号栈,并且要留在符号栈中,等与右括号出现;
B:为保证左括号留在符号栈中,其优先级就默认返回-1,即最小;
C:遇到右括号时,就计算左右括号之间的数据,最后把结果保存到数栈,然后弹出左括号;
D:为增强程序的可读性,将代码相同的部分定义为方法;
-
代码实现
package cn.itcast_02;
import java.util.Stack;
/*
* 需求:
* A:要求输入的是中缀表达式,如(10+2)*3-10/5-(8/4);
* B:要求输入运算符只有加、减、乘、除和小括号;
* C:返回中缀表达式的结果;
*
*/
public class TestDemo {
public static void main(String[] args) {
// 定义字符串,用于存放中缀表达式
String expression = "(10+2)*3-10/5-(8/4)";
// 定义两个栈对象,即数栈和符号栈
Stack<Integer> numStack = new Stack<Integer>();
Stack<Character> operStack = new Stack<Character>();
// 利用循环对字符串进行扫描
scanString(numStack, operStack, expression);
// 表达式扫描完毕后,进行运算
while (true) {
// 如果符号栈为空,则计算结束,数栈中只有1个值
if (operStack.isEmpty()) {
break;
}
// 符号栈非空,则继续运算
function(numStack, operStack);
}
System.out.printf("表达式的结果%s = %d", expression, numStack.pop());
}
// 定义字符串扫描函数
public static void scanString(Stack<Integer> numStack, Stack<Character> operStack, String expression) {
// 定义相关变量
char oper = ' ';
String keepNum = "";// 用于拼接多位数
for (int i = 0; i < expression.length(); i++) {
// 获取该字符串的每一个字符
char ch = expression.substring(i, i + 1).charAt(0);
// 判断该字符是运算符还是数字
if (isOper(ch)) {
// 判断符号栈是否为空
if (operStack.isEmpty()) {
operStack.push(ch);
} else if (ch == '(') {
// 符号栈不为空,ch为'('的情况
operStack.push(ch);
} else if (ch == ')') {
// 符号栈不为空,ch为')'的情况
oper = operStack.peek();
while (oper != '(') {
// 将栈中左括号上面的运算符弹出并运算
function(numStack, operStack);
oper = operStack.peek();
}
operStack.pop();// 弹出左括号
} else {
// 符号栈非空,且ch不是 '(' 或 ')' 的情况
// 将当前ch与符号栈中运算符的优先级进行比较,可能比较多次,故选择while
while (!operStack.isEmpty() && priority(ch) <= priority(operStack.peek())) {
function(numStack, operStack);
}
operStack.push(ch);
}
} else {
// 扫描到的是数字字符
// 将ch转换为字符串,目的扫描多位数
keepNum += ch;
// 判断ch是否为字符串最后一个元素
if (i == expression.length() - 1) {
numStack.push(Integer.parseInt(keepNum));
} else {
// 判断下一个字符是否为数字,是就继续扫描,否则入栈
oper = expression.substring(i + 1, i + 2).charAt(0);
if (isOper(oper)) {
numStack.push(Integer.parseInt(keepNum));
keepNum = "";// 这里很重要
}
}
}
}
}
// 定义计算后出栈入栈功能
public static void function(Stack<Integer> numStack, Stack<Character> operStack) {
int num1 = 0;
int num2 = 0;
char oper = ' ';
int res = 0;
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = cal(num1, num2, oper);
numStack.push(res);
}
// 判断字符是否为运算符
public static boolean isOper(int ch) {
return ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')';
}
// 自定义运算符优先级
public static int priority(int ch) {
if (ch == '+' || ch == '-') {
return 0;
} else if (ch == '*' || ch == '/') {
return 1;
} else {
return -1;// 这里左括号为保证能够在堆栈里,优先级设为最低
}
}
// 定义计算函数
public static int cal(int num1, int num2, char ch) {
int res = 0;
switch (ch) {
case '+':
res = num1 + num2;
break;
case '-':
res = num2 - num1;// 这个地方就想象2-1时的情景
break;
case '*':
res = num1 * num2;
break;
case '/':
res = num2 / num1;
break;
default:
break;
}
return res;
}
}
三、总结
A:程序的可读性比较差,只要充斥着大量if-else,调试起来就麻烦,烦人。