1. 简介
很多男孩子小时候肯定玩过玩具枪,如果你小时候没有玩过那你一定在游乐园里玩过玩具枪打气球的游戏。我小时候玩玩具枪时最喜欢的就是收集子弹,捡邻居家孩子打完后的子弹,这样可以重复利用,我自己也不用花钱买子弹。
想想一下玩具枪上子弹的过程,先取下弹夹,然后将一颗颗的子弹放到弹夹里,然后先被放进去的子弹最后被射出来,最后放进去的子弹最先被射出来。而这种先进后出,后进先出的就是栈。就像玩具枪上子弹一样。每一次取出来的子弹都是最上面的一个,而放入一个子弹也是在最上面。
栈结构如下。
其中top就是指向栈顶的指针,所谓栈顶就是当前栈最上面的数据,也就是当前弹夹中最上面的子弹,而栈底就是最下面的数据,也就是当前弹夹中最下面的子弹。
从上面的图可以发现每一个数据都有前驱和后继,那么实际上栈也是一种线性表,他是一种特殊的线性表,他是只能在一端进行新增数据和获取数据,也没有链表的插入数据和根据某个结点删除数据,而且无论是取数据或者新增都需要在栈顶操作。
既然栈满足线性表那么他一样可以有顺序存储结构和链式存储结构,顺序存储结构我们可以通过数组进行实现,栈底就是索引为0,而栈顶则是当前最新的数据。而使用链式存储可以使用链表,而栈底就是第一个结点,而栈底同样也是最新的数据地址。而顺序存储相比链式存储而言实现起来相对简单一点,但是因为是数组实现所以需要手动扩容,那么就会浪费一些没有使用的空间,而链式存储不需要扩容所以内存的占用没有顺序存储那么大,但是由于每次取出数据时都需要移动top(也就是栈顶),必须要找到上一个数据的地址所以需要遍历链表,那么效率比顺序存储低很多,前提是使用的非双向链表,如果使用双向链表在取数据(出栈)时都是O(1),而使用非双向链表时链表取数据(出栈)则是O(n)。
2. 使用顺序存储结构实现栈
package netty;
/**
* 栈的顺序存储
* @author damao
* @date 2019-11-27 10:20
*/
public class OrderStack<T>{
/**
* 默认长度为20
*/
private Data<T>[] stack = new Data[DEFAULT_CAPACITY];
private Object object = new Object[10];
/**
* 默认容量为20
*/
private static int DEFAULT_CAPACITY = 2;
/**
* 用于指向栈顶,默认指向数组的第0位
*/
private int top = 0;
public boolean push(T t){
capacityExpansion ();
Data data = new Data ();
data.setData (t);
stack[top] = data;
top++;
return true;
}
public T pop(){
top--;
if(top == -1){
throw new RuntimeException ("Stack is empty");
}
T data = stack[top].getData ();
stack[top] = null;
return data;
}
public void capacityExpansion() {
if(top >= DEFAULT_CAPACITY){
Data<T>[] newstack = new Data[ DEFAULT_CAPACITY *= 2];
System.arraycopy (stack,0,newstack,0,stack.length);
stack = newstack;
}
}
public static class Data<T>{
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data=data;
}
}
}
测试结果如下。
3. 使用链式存储结构实现栈
此处使用的是单向链表,非双向链表。
package netty;
/**
* 栈的链式存储
* @author damao
* @date 2019-11-27 10:20
*/
public class LinkedStack<T>{
/**
* 用于存放栈顶的地址
*/
private Data<T> topData = null;
/**
* 链表的起始地址
*/
private Data<T> fristData = null;
public boolean push(T t) {
if(topData != null){
Data<T> currentData = new Data(null, t);
topData.next = currentData;
topData = currentData;
return true;
}
Data<T> currentData = new Data(null, t);
fristData = topData = currentData;
return true;
}
public T pop(){
if(topData == null){
throw new RuntimeException("Stack is empty");
}
Data<T> result = topData;
if(fristData == topData){
topData = null;
return result.data;
}
mobilePointerTop();
return result.data;
}
public void mobilePointerTop(){
Data<T> newTopData = fristData;
Data<T> previous = fristData;
while (newTopData != topData){
previous = newTopData;
newTopData = newTopData.next;
}
topData = previous;
}
public static class Data<T>{
Data<T> next;
private T data;
public Data(Data<T> next, T data) {
this.next=next;
this.data=data;
}
}
}
测试结果如下