目录:
- 环境及配置
- 原理及步骤
- 代码
- 运行结果
1. 环境及配置:
- 语言:Java
- IDE:eclipse
- 所用数据结构:
Stack(栈)
2.原理及步骤:
中缀表达式就是我们日常生活中使用的习惯性的表达式,例如:1 + 2 * 3
,这里我们实现从键盘接受一段中缀表达式,并对其进行求值,输出结果。
具体步骤如下:
- 首先从键盘接受一段中缀表达式。使用标准键盘输入
System.in
构造输入流,然后使用nextLine()
接收一行字符串。 - 为了便于提取有效字符,这里构造了一个方法,返回一个字符串数组:
输入的中缀表达式有可能不符合规范,不便于提取存储每个有效字符,这里需要我们自己构造一个方法来将其格式化,便于提取单个有效字符。
- 使用2个栈来实现,一个存储操作数,一个存储操作符
- 遍历有效字符数组,按照下面的原则:
1.左括号'('入栈
2.数字'0'~'9'入栈
3.遇到运算符号'+'、'-'、'*'、'/'时,将栈内优先级大于等于该运算符的符号出栈参与运算,结果入栈
4.如果遇到有括号')',则对栈内所有的数字进行运算 - 这里计算也构造一个方法方便操作
- 最后,循环对操作数栈进行运算,直到操作符栈为空
3.代码:
Test.java
import java.util.Scanner;
// 中缀算术表达式求值
public class ExpressionEvaluation {
public static void main(String[] args) throws Exception {
Scanner input = new Scanner(System.in);
LinkedStack<Integer> value = new LinkedStack<>();
LinkedStack<Character> operation = new LinkedStack<>();
System.out.print("请输入一个中缀表达式:");
String[] expression = formatInput(input.nextLine());// 从键盘输入一个中缀表达式,然后格式化后存储为一个字符串数组
// 首先,遍历expression数组,同时以下列原则进行操作,然后,对栈内的剩余数字进行运算,直到操作符栈为空
for (String s : expression) {
if (s.length() == 0)// 因为格式化的时候是在非数字符号前后加空格,所以会存在分割出来是空的情况
continue;
else if (s.charAt(0) == '+' || s.charAt(0) == '-' || s.charAt(0) == '*' || s.charAt(0) == '/') {// 遇到运算符,将栈内优先级大于等于自己的符号拿出来参与计算
while (!operation.isEmpty() && priorityIsBiggerOrTheSame(operation.peek() + "", s)) {// 循环直到栈空或者遇到优先级较小的符号
compute(value, operation);
}
operation.push(s.charAt(0));// 当前符号入栈
}
else if (s.charAt(0) == '(')// 右括号入栈
operation.push('(');
else if (s.charAt(0) == ')') {// 遇到左括号,将栈内符号全部出栈参与运算
while (operation.peek() != '(')// 循环直到遇到左括号
compute(value, operation);
operation.pop();// 左括号出栈
}
else
value.push(Integer.parseInt(s));// 数字入栈
}
while (!operation.isEmpty())// 最后将所有的符号出栈参与运算
compute(value, operation);
System.out.println("计算结果是:" + value.pop());
}
// 提取出操作数栈的前2个数,操作符栈的栈顶操作符,运算
public static void compute(LinkedStack<Integer> value, LinkedStack<Character> operation) {
int v1 = value.pop();
int v2 = value.pop();
char op = operation.pop();
switch (op) {
case '+':
value.push(v2 + v1);
break;
case '-':
value.push(v2 - v1);
break;
case '*':
value.push(v2 * v1);
break;
case '/':
value.push(v2 / v1);
break;
}
}
// 格式化中缀表达式输入,即在符号前后添加空格,便于后面的分隔操作
public static String[] formatInput(String s) {
String temp = "";
/*
* 提取出表达式中有效的字符(非空格),然后在字符后面统一添上一个空格,方便后面使用静态方法String.split()来
* 例如:1 *(2+ 3) /4 -----> 1 * ( 2 + 3 ) / 4
* 你也可以直接使用List类来存储提取的有效字符 总之最后的目的就是返回一个数组,其中存储的是有效字符
*/
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '+' || c == '-' || c == '*' || c == '/')
temp += " " + c + " ";
else
temp += c;// 数字不用加空格
}
return temp.split(" ");// 分割
}
// 优先级判断,a是否大于b
public static boolean priorityIsBiggerOrTheSame(String a, String b) {
String s = "+- */";
return (s.indexOf(a) - s.indexOf(b)) >= 2;
}
}
ArrayStack.java
package stack;
import java.util.Iterator;
import java.util.Scanner;
/**
* 使用数组实现的mini版的栈,只保留需要的方法
* @author Super233
* @param <E>泛型参数
*/
public class ArrayStack<E> implements Iterable<E> {
private final int MAX_SIZE = 16;// 初始大小定为16
private E[] data = null;// 数据域
private int size = 0;// 栈中元素个数
// 默认无参构造方法
public ArrayStack() {
data = (E[]) new Object[MAX_SIZE];
}
// 含参构造方法,实现栈的初始大小自定义
public ArrayStack(int cap) {
data = (E[]) new Object[cap];
}
// 入栈
public void push(E e) {
ensureCapcity();
data[size++] = e;
}
// 出栈
public E pop() {
E top = data[size - 1];
data[size - 1] = null;
size--;
return top;
}
// 获得栈顶元素
public E peek() throws Exception {
if (isEmpty())
throw new Exception("The stack is Empty!");
else
return data[size - 1];
}
// 实现栈的大小不够时自动增加
public void ensureCapcity() {
if (size == data.length) {
E[] temp = (E[]) new Object[data.length * 2];
for (int i = 0; i < data.length; i++)
temp[i] = data[i];
data = temp;
}
}
// 判空
public boolean isEmpty() {
return size == 0;
}
// 重写的toString()方法
@Override
public String toString() {
String result = "data: ";
for (int i = 0; i < size; i++)
result += data[i] + " ";
return result;
}
// 继承接口Iterable后需要重写的方法,返回一个迭代器
@Override
public Iterator<E> iterator() {
return new ReverseIterator();
}
// 内部类迭代器,从栈顶到栈底访问数据
private class ReverseIterator implements Iterator<E> {
private int current = size - 1;// current表示当前下标,倒序遍历,所以current初始化为size-1
@Override
public boolean hasNext() {
return current >= 0;// 倒序遍历,为了实现foreach,这里应该是>=0
}
@Override
public E next() {
return data[current--];
}
}
}
4.运行结果:
PS:第一次写博客,献上米老鼠镇楼,寒假快乐
_-~~~-_ _-~~~-_
/~ ~\ : , \
' ~ , |: :
{ /~~\ :--~""""\.: :
\ (... : /^\ /^\\ ;
~\_____ | | | |:~
/ |__O|_|O|;
( / O \
\ ( `\_______/) ~~~❤
`\ \ /
) ~-------~'\
/ \
: ❤ ||
| | ||
| |.======[]==+'|
(~~~~) | |~)
/ \ | | \
~\ \___/)______/^\__|_/
`\\ // | | | |
`\\__//' | | | |
~~ (~~~~) (~~~)
/ =\..' =_
|__________)________)