栈的定义
栈是限定仅在一段进行插入和删除的线性表。虽然这个限制减少了栈的灵活性,但也使得栈更有效,更容易实现。栈也被叫做LIFO线性表(即后进先出表),习惯上称栈的可访问元素为栈顶元素,元素的插入称为入栈(push),元素的删除称为出栈(pop)。栈可分为顺序栈和链式栈,顺序栈底层是一个数组,栈顶元素就是数组尾部元素;而链式栈是以链表为基础。
栈的链式实现
栈的链式实现实质是对于链表的简化实现,由于栈只需要在一端进行删除和插入操作,因此只需要一个头部节点指向栈顶元素即可。栈的实现如下
import java.util.EmptyStackException;
public class Stack<T> {
//定义链表节点
static class Node<T> {
//使用泛型
T t;
Node<T> next;
private Node(T t) {
this.t = t;
next = null;
}
}
//栈顶元素
private Node<T> top;
//栈中元素个数
private int size;
public Stack() {
size = 0;
}
public boolean isEmpty() {
return size > 0 ? false : true;
}
//入栈操作
public void push(T t) {
//如果栈是空的话,需要对top进行初始化操作
if (size == 0) {
top = new Node<T>(t);
size++;
return;
}
//定义一个tmp临时变量指向旧的栈顶元素
Node<T> tmp = top;
top = new Node<T>(t);
top.next = tmp;
size++;
}
//弹出栈顶元素并返回该元素
public T pop() {
if (size == 0) {
throw new EmptyStackException();
}
T tmp = top.t;
top = top.next;
size--;
return tmp;
}
//返回栈顶元素但不弹出
public T peek() {
//如果栈中无元素则抛出异常
if (size == 0) {
throw new EmptyStackException();
}
return top.t;
}
}
算术表达式的计算
平时我们所接触的算术表达式从形式上看都是中缀表达式,这是一种我们容易接受的形式,但这种形式对于计算机来说就不是那么一回事了。通常我们都需要将中缀表达式转为后缀或前缀表达式然后再进行求值。下面我以中缀转后缀为例。
中缀表达式转后缀表达式
这个过程需要用到两个,一个是运算符栈,另一个是后缀表达式栈(用于存放结果),具体操作如下
1. 从左往右扫描中缀表达式
2. 如果遇到运算符(+,-,*,/),如果运算符栈为空则直接将运算符压人,如果不为空将该运算符和运算符栈中的栈顶元素进行优先级比较,如果该运算符优先级较大,那么直接将该运算符压人运算符栈;否则,依次从运算符栈中弹出栈顶元素并压人后缀表达式栈,直到运算符栈栈顶元素优先级小于我们扫描到的元素。(其实也就一句话,保证运算符栈的优先级有序,使栈顶元素优先级最大)
3. 如果遇到“(”,那么直接压人运算符栈
4. 如果遇到“)”,那么需要弹出运算符栈中的元素并压入后缀表达式栈直到遇到“(”,然后将“(”弹出
5. “(”只有“)”能使它弹出,其他运算符均不能使其弹出
6. 如果遇到数字直接压人后缀表达式栈
7. 扫描完毕时依次将运算符栈中的符号弹出压