1.什么是栈?
栈(stack)是一个有序线性表,只能在表的一端(称为栈顶,top)执行插入和删除操作。最后插入的元素将第一个被删除。所以,栈也称为后进先出(Last In First Out,LIFO)或先进先出(First In Last Out,FILO)线性表。
两个改变栈操作都有专用名称。一个称为入栈(push),表示在栈中插入一个元素;另一个称为出栈(pop),表示从栈中删除一个元素。试图对一个空栈执行出栈操作称为下溢(underflow);试图对一个满栈执行入栈操作称为溢出(overflow)。通常,溢出和下溢均认为是异常。
2.栈抽象数据类型
下面给出栈抽象数据类型中的操作。为了简单起见,假设数据类型为整型。
(1)栈的主要操作
void push(int data):将data(数据)插入栈。
int pop():删除并返回最后一个插入栈的元素。
(2)栈的辅助操作
int top():返回最后一个插入栈的元素,但不删除。
int size():返回存储在栈中元素的个数。
int isEmpty():判断栈中是否有元素。
int isStackFull():判断栈中是否存满元素。
3.异常
在执行操作时发生的错误称为异常。当操作不能执行时,会“抛出”异常。在栈抽象数据类型中,pop操作和top操作在栈空时是不能执行的。试图对一个空栈执行pop(或top)操作会抛出异常。试图对一个满栈执行push操作也会抛出异常。
4.应用
栈在下列应用中具有重要的作用
(1)直接应用
符号匹配
中缀表达式转换成后缀表达式
计算后缀表达式
实现函数调用(包括递归)
求范围误差(极差)(在股票市场中求极差)
网页浏览器中已访问页面的历史记录(后退(back)按钮)
文本编辑器中的撤销(undo)序列
HTML和XML文件中的标签(tag)匹配
(2)间接应用
作为一个算法的辅助数据结构(例如,树遍历算法)
其他数据结构的组件(例如,模拟队列)
5.实现
栈抽象数据类型有多种实现方式。下面是常用的方法。
基于简单数组的实现方法
基于动态数组的实现方法
基于链表的实现方法
(1)简单数组的实现
public class ArrayStack {
private int top;
private int capacity;
private int[] array;
public ArrayStack(){
capacity=2;
array=new int[capacity];
top=-1;
}
public boolean isEmpty(){
//if the condition is true then 1 is returned else 0 is returned
return (top==-1);
}
public boolean isStackFull(){
//if the condition is true then 1 is returned else 0 is returned
return (top==capacity-1);
}
public void push(int data){
if(isStackFull()) System.out.println("Stack Overflow");
else array[++top]=data;
}
public int pop(){
if(isEmpty()){
System.out.println("Stack is Empty");
return 0;
}
else return (array[top--]);
}
public void deleteStack(){
top=-1;
}
}
局限性:栈的最大空间必须预先声明且不能改变。试图对一个满栈执行入栈操作将产生一个针对简单数组这种特定实现栈方式的异常。
(2)动态数组的实现
在上述基于简单数组的栈实现方法中,采用一个下标变量top,它始终指向栈中最新插入元素的位置。当插入(或push)元素时,先增加下标变量top的值然后在数组中该下标位置存储新元素。类似的当删除(或pop)元素时,先获取下标变量top位置的元素,然后减小变量top的值。当下标变量top的值等于-1时,表示栈为空。然而仍然需要解决的一个问题是,在固定大小的数组中,如何处理所有空间都已经保存了栈元素这种情况。
a.解决方法1:当栈满时,每次将数组的大小加一。
这种增加数组大小的方法开销太大。
b.解决方法2:重复倍增
可以使用数组倍增技术来提高性能。如果数组空间已满,新建一个比原数组空间大一倍的新数组,然后复制元素。采用这种处理方式,执行n次push操作的时间开销为n。
public class DynArrayStack {
private int top;
private int capacity;
private int[] array;
public DynArrayStack(){
capacity=1;
array=new int[capacity];
top=-1;
}
public boolean isEmpty(){
//if the condition is true then 1 is returned else 0 is returned
return (top==-1);
}
public boolean isStackFull(){
//if the condition is true then 1 is returned else 0 is returned
return (top==capacity-1);
}
public void push(int data){
if(isStackFull()) doubleStack();
array[++top]=data;
System.out.println("top="+top);
}
public void doubleStack(){
int newArray[]=new int[capacity*2];
System.arraycopy(array,0,newArray,0,capacity);
capacity=capacity*2; System.out.println("capacity="+capacity);
array=newArray;
}
public int pop(){
if(isEmpty()){
System.out.println("Stack is Empty");
return 0;
}
else return (array[top--]);
}
public void deleteStack(){
top=-1;
}
}
注意:倍增太多可能导致内存溢出。
(3)链表实现
使用链表也可以实现栈。通过在链表的表头插入元素的方式实现push操作,删除链表的表头结点(栈顶结点)实现pop操作。
public class LLNode {
private int data;
private LLNode next;
public LLNode(int data){
this.data=data;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public LLNode getNext() {
return next;
}
public void setNext(LLNode next) {
this.next = next;
}
}
public class LLStack {
private LLNode headNode;
public LLStack(){
this.headNode=null;
}
public void Push(int data){
if(headNode==null){
headNode=new LLNode(data);
}
// else if(headNode.getData()==null){
// headNode.setData(data);
// }
else{
LLNode llNode=new LLNode(data);
llNode.setNext(headNode);
headNode=llNode;
}
}
public Object top(){
if(headNode==null) return null;
else return headNode.getData();
}
public int pop(){
if(headNode==null){
System.out.println("Stack empty");
return -1;
}else{
int data=headNode.getData();
headNode=headNode.getNext();
return data;
}
}
public boolean isEmpty(){
if(headNode==null) return true;
else return false;
}
public void deleteStack(){
headNode=null;
}
}