参考资料:《大话数据结构》
栈(Stack)
说起栈,应该都会想起“先进后出”这四个字,之前在考计算机二级的时候看过一些数据结构的知识,当时也就记住了这四个字,也不太清楚具体可以在哪些方面应用,直到后来系统的学习了数据结构,才发现原来可以这么搞。
1. 定义
栈是限定仅在表尾进行插入和删除操作的线性表。故栈是线性结构,可以想象成水杯,水只能从一个口进出。把允许插入和删除的一端(杯口)称为栈顶,另一端(杯底)称为栈底。向栈中插入元素称为压栈,也称进栈、入栈,从栈中删除元素称为弹栈,也称出栈。栈顶指针(top)永远指向最靠近栈顶的元素,压栈和弹栈都是根据栈顶指针来进行操作。
由于栈也是一个线性表,故其有顺序储存结构和链式储存结构
2.栈的顺序储存结构
栈的顺序储存结构,其底层是用数组实现的,我们把数组下标为0的一端规定为栈底。栈顶指针可为数组的下标,规定当栈为空时,栈顶指针top的值为-1(处于栈底位置),且top的值要小于等于数组长度减一(下图数组长度为5)。
则定义一个栈需要两个参数:数组和栈顶指针。
int[] Stack = new int[len]; //len为数组长度
int top = -1; //top为栈顶指针,一般初始化为-1
压栈
在进行压栈操作之前,我们应先判断下栈是否满了(如果水杯中的水满了,还怎么往里面倒水)。方法其实很简单,根据栈顶指针的定义(永远指向最靠近栈顶的元素),只要判断top是否和数组长度减一相等即可,若相等及说明栈满,不等则不满。
public boolean isFull(){
if(top == len - 1)
return true;
else
return false;
}
现在进行压栈操作,如下图,假设数组长度为5,栈中现有三个元素(a1,a2和a3),先往栈中压入a4。我们应先将栈顶指针上移一位,然后再把a4添加到栈中。
public boolean push(int val) { //val为要添加的值
if(isFull())
return false; //栈满,无法压栈
arr[++top] = val; //栈顶指针先上移一位,在入值
return true;
}
弹栈
和压栈一样,在弹栈之前应该检查一下栈是否为空(水杯里面没有水,怎么往外倒)。栈为空时,其栈顶指针top为-1,故其判断非常简单。
public boolean isEmpty() {
if(top == -1)
return true;
else
return false;
}
现在进行弹栈操作,压栈的时候是栈顶指针先上移一位,然后再添加相应元素,而弹栈恰恰相反,我们先取出要删除的值(此时栈顶指针指向的值),再把栈顶指针下移一位(这样保证删除的元素为栈顶元素,满足先进后出,同时保证了弹栈之后栈顶指针始终指向最靠近栈顶的元素)。
public boolean pop(){
if(isEmpty())
return false; //栈为空
val = arr[top--]; //先删除栈顶元素,再将栈顶指针下移一位
return true;
}
3.栈的链式储存结构(链栈)
栈的链式存储结构,其底层是链表。由于链表有头指针,所以我们把栈顶放在链表的头部,而头指针可看成栈顶指针(无头结点)。栈为空时,栈顶指针为null。
压栈
由于其底层为链表,所以不存在满的情况。下图s为新入栈的结点,有点像链表的头插法。
public void push(int e){
Node s = new Node(val); //先生成一个新结点s
s.data = e;
s.next = top; //top现在仍指向原来的栈顶元素ai
top = s; //top指向s,s现在为栈顶元素(top始终指向栈顶元素)
}
弹栈
进行弹栈操作之前,先要判断栈是否为空,即top是否为null。
public boolean isEmpty() {
if(top == null)
return true;
else
return false;
}
现在进行弹栈操作。如下图,弹出栈顶元素ai。
public boolean pop() {
if(isEmpty())
return false; //栈为空
val = top.data; //出栈的值赋给val,top此时指向要删除的元素
Node p = top.next; //p指向top的下一个结点(栈顶元素的下面一个元素)
top = p; //top指向下一个结点
return true;
}
关于栈的应用
主要是利用其先进后出的特点。
- 函数的调用(学Java的时候画内存图时,有一个栈内存,对于方法的压栈和弹栈)
- 对于四则表达式求值(后面会有介绍)