栈
1、抽象数据类型
栈是一种特殊的线性表,其插入和删除操作只能在线性表的一端进行。允许操作的一端称为栈顶(top),不允许操作的一端称为栈底(bottom),若栈中没有元素,则为空栈。栈是实现嵌套调用机制的基础,典型的比如系统的方法调用。当一个方法A需要调用方法B时,系统会将方法A的相关信息(地址、参数等)入栈,称为保护现场。当方法B执行完毕时,再将方法A出栈,称为恢复现场。
由于栈只允许在线性表的一端进行,新入栈的元素即成为栈顶元素,要出栈的元素也是最新入栈的元素,所以栈的操作顺序是后进先出(LIFO)。以下是栈的抽象数据类型定义:
1 package list.impl; 2 3 /** 4 * 栈接口 5 */ 6 public interface IStack<T> { 7 /** 8 * 判断栈是否为空 9 * @return 10 */ 11 boolean isEmpty(); 12 13 /** 14 * 元素入栈 15 * @param x 16 */ 17 void push(T x); 18 19 /** 20 * 元素出栈 21 * @return 当前栈顶元素 22 */ 23 T pop(); 24 25 /** 26 * 获取栈顶元素,元素不出栈 27 * @return 当前栈顶元素 28 */ 29 T get(); 30 }
同上一篇博文的线性表,我们也用顺序方式和链式来实现栈。
2、栈的顺序表示和实现
栈的顺序操作
类似于线性表的实现,栈的顺序实现是比较简单的,以下是完整的代码:
package list.impl; /** * 栈的顺序表示和实现 */ public class SeqStack<T> implements IStack<T> { private T[] elements; // 存储栈元素 private int top; // 栈顶元素下标 public SeqStack(int size) { elements = (T[])new Object[size]; top = -1; } public SeqStack() { this(64); } @Override public boolean isEmpty() { return top == -1; } @Override public void push(T x) { if (x == null) return; // 若栈满,则扩充容量 if (top == elements.length - 1) { T[] old = elements; elements = (T[])new Object[elements.length * 2]; for (int i = 0, size = old.length; i < size; i++) { elements[i] = old[i]; } } top++; elements[top] = x; } @Override public T pop() { return top == -1 ? null : (T)elements[top--]; } @Override public T get() { return top == -1 ? null : (T)elements[top]; } }
栈只在一端进行操作,所以push,get和pop操作的时间复杂度都是O(1)。但是如果当栈需要扩充容量时,push操作的时间复杂度是O(n)。
3、栈的链式表示和实现
当栈为空时,top == null。此时头结点的意义不大。实现没有难的地方,以下是完整的代码:
package list.impl; /** * 栈的链式表示和实现 */ public class LinkedStack<T> implements IStack<T> { private Node<T> top; // 栈顶元素 public LinkedStack() { top = null; } @Override public boolean isEmpty() { return top == null; } @Override public void push(T x) { if (x != null) { top = new Node<T>(x, top); } } @Override public T pop() { if (isEmpty()) return null; T result = top.data; top = top.next; return result; } @Override public T get() { return top == null ? null : top.data; } private class Node<E> { E data; Node<E> next; Node(E data, Node<E> next) { this.data = data; this.next = next; } Node() { this(null, null); } } }