前面已经采用数组的形式实现了栈这种逻辑结构,因此在这里将采用另一种物理存储结构,单链表来实现栈,并基于单链表的形式,完成逆波兰表达式的计算。
1. 使用单链表实现栈
实现了最基本的入栈、弹栈、判空、获取长度、迭代器遍历功能。其中入栈采用的是“头插法”,迭代器是实现Iterable
接口。
实现代码:
public class Stack<T> implements Iterable<T> {
// 常用变量
private Node<T> head;
private int N;
public Stack() {
this.head = new Node(null, null);
this.N = 0;
}
public boolean isEmpty() {
return this.N == 0;
}
public int getLen() {
return this.N;
}
// 使用头插法完成栈的构建
public void push(T elem) {
Node<T> newNode = new Node<>(elem, head.next);
head.next = newNode;
this.N++;
}
// 删除元素
public T pop() {
// 判断栈是否为空
if (isEmpty()) {
return null;
}
T elem = this.head.next.elem;
this.head.next = this.head.next.next;
this.N--;
return elem;
}
// 实现迭代器方法
@Override
public Iterator<T> iterator() {
return new MyIter();
}
private class MyIter implements Iterator<T> {
// 定义一个成员变量,用于指向栈的头节点
private Node<T> node = head;
@Override
public boolean hasNext() {
return node.next != null;
}
@Override
public T next() {
node = node.next;
return node.elem;
}
}
// 内部结点类
private class Node<T> {
private T elem;
private Node<T> next;
public Node(T elem, Node<T> next) {
this.elem = elem;
this.next = next;
}
}
}
2. 借助栈实现逆波兰表达式的计算
- 前面已经讲解过波兰表达式、逆波兰表达式,具体参见这里几个小概念。
- 计算机对于前缀和后缀表达式计算起来很简单,而对于中缀表达式,则需要转换为前缀或者后缀表达式再进行计算,具体的转换规则参考这个博客。前缀、中缀、后缀
- 当获取到后缀表达式之后,就可以借助栈,来实现表达式结果的计算。
- 逆波兰(后缀)表达式的计算思路:
- 先创建一个存放数据的栈,简称“数栈”。
- 从左往右遍历表达式,如果是数字,则直接入数栈,如果是符号,则依次从栈中弹出两个数字,进行计算,计算的结果存入到“数栈”中。用后弹出的一个数 运算 前面弹出的一个数,顺序很重要!!!
- 重复上面一个步骤,直到遍历完成整个表达式,最后栈中的元素就是整个表达式的结果。
- 实现代码:
public class PostfixExpression {
public static void main(String[] args) {
// 中缀表达式为:3+(9-3)*4-5 ----> 后缀表达式为 3 9 3 - 4 * + 5 -
String[] strs = {"3", "9", "3", "-", "4", "*", "+", "5", "-"};
int result = getResult(strs);
System.out.println("最后的结果是" + result);
}
public static int getResult(String[] strs) {
// 初始化一个数栈,用于存储数字
Stack<Integer> stack = new Stack<>();
// 遍历后缀表达式
// 用两个数来记录栈中弹出的元素
Integer o1 = null;
Integer o2 = null;
for (int i = 0; i < strs.length; i++) {
// 1. 如果是数字,则直接入栈,如果是符号,就弹出栈中的两个元素,进行计算,将结果再入栈
if (isNumber(strs[i])) {
stack.push(Integer.parseInt(strs[i]));
} else {
// 判断栈是否为空,为空,则证明表达式错误了
if (stack.isEmpty()) {
System.out.println("表达式错误,无法计算!");
throw new RuntimeException("表达式错误!");
}
o1 = stack.pop();
o2 = stack.pop();
// 计算值,并存入到栈中
cal(stack, o1, o2, strs[i]);
}
}
// 循环完成,计算也就完成了,最后取出栈中元素,即为最后结果
return stack.pop();
}
public static boolean isNumber(String oper) {
return Pattern.matches("\\d+", oper);
}
public static void cal(Stack<Integer> stack, Integer o1, Integer o2, String oper) {
// 用于存放弹出的两个值的计算结果
int result = 0;
// 判断表达式中的符号类型,并进行相应计算,将结果入栈
switch (oper) {
case "+":
result = o2 + o1;
stack.push(result);
break;
case "-":
result = o2 - o1;
stack.push(result);
break;
case "*":
result = o2 * o1;
stack.push(result);
break;
case "/":
result = o2 / o1;
stack.push(result);
break;
}
}
}