如何实现栈这种数据结构
零 栈这种数据结构的特征
- 依然是一种线性数据结构;
- 规定只能在一端操作的数据结构;
- 后进先出,First In Last Out;
- 看似简单,应用却十分广泛的一种数据结构;
一 栈的实现思路
我们在前面自己动手实现了一个支持泛型类的数组,那么我们就可以考虑通过数组这种线性数据结构来实现一个栈,顺序栈。当然,我们也可以用链表实现一个栈,称之为链式栈。
先定义一个支持泛型的栈的接口,包含入栈操作、出栈操作、查看栈顶元素、查看栈的大小、栈是否为空的几个接口方法。外带2个构造方法。
然后,定义一个底层用数组实现的栈,来实现这个接口。
二 栈代码实现
1定义栈的接口代码
public interface Stack<E>{ //入栈操作 void push(E e); //出栈操作,出栈之后,栈顶元素已经从栈中删除 E pop(); //查看栈顶元素,该元素依然存在于栈中 E peek(); //查看栈的元素个数 int getSize(); //栈是否为空 boolean isEmpty(); }
2 底层用数组实现的顺序栈
public class ArrayStack<E> implements Stack<E>{ //把我们前面实现的数组作为栈的底层存储,做成栈的成员变量 private Array<E> array; //构造方法 public ArrayStack(int capacity){ array=new Array<>(capacity); } //default constructor public ArrayStack(){ array=new Array<>(); } @Override public void push(E e){ //调用数组的addLast()方法,这样就只能在一端插入元素了。 //这样封装的话,用户使用栈的时候,就不能随意插入元素了,实现了栈只能在一端操作的目的 array.addLast(e); } @Override public E pop(){ //直接调用数组的removeLast()方法,又封装成了只能在一端删除元素 return array.removeLast(); } @Override public E peek(){ //调用数组的getLast()方法,封装成只能在一端查看元素值 retrun array.getLast(); } @Override public int getSize(){ return array.getSize(); } @Override public boolean isEmpty(){ return array.isEmpty(); } @Override public String toString(){ StringBuilder stringBuilder=new StringBuilder(); stringBuilder.addpend("stack:["); for(int i=0;i<array.size;i++){ stringBuilder.append(array.get(i)); //当没有遍历到数组最后一个元素时,拼接一个" , ", if(i!=array.getSize()-1){ stringBuilder.append(" , "); } } //加个top字符串,表示栈顶的意思 stringBuilder.append("] top"); return stringBuilder.toString(); } public static void main(String[] args){ //初始化1个泛型是Integer类型的栈 ArrayStack<Integer> arrayStack=new ArrayStack<>(); //初始化栈的大小为0,因为底层数组也是空的,没有任何元素。但是我们前面实现的数组的初始化容量是10。在栈这边是看不到的,隐藏的。我们想查看的话,可以通过arrayStack.array.getCapacity()来调用。 System.out.println(arrayStack.getSize()); System.out.println(arrayStack.array.getCapacity()); //初始化栈为空 System.out.println(arrayStack.isEmpty()); //向栈中插入元素,压栈操作 for(int i=0;i<5;i++){ arrayStack.push(i); } System.out.println(arrayStack.getSize()); System.out.println(arrayStack); //出栈操作 System.out.println(arrayStack.pop()); //查看栈顶元素 System.out.println(arrayStack.peek()); System.out.println(arrayStack); } } //结果: 0 10 true 5 Stack [0 , 1 , 2 , 3 , 4] top 4 3 Stack [0 , 1 , 2 , 3] top
三 复杂度分析
1空间复杂度
栈除了底层存储的数组占据的存储空间之外,对栈的出栈、入栈操作,不需要更多额外的存储空间,所以其空间复杂度是O(1)。
2 时间复杂度
由于我们是用支持动态扩展的数组来实现的栈,因此,当不需要扩容的话,其时间复杂度就是O(1),如果需要扩容的话,因为需要将原数组中的元素遍历一遍并copy到新数组,所以为O(n)。
同理,对于出栈操作,不缩容的情况为O(1)的时间复杂度,缩容则为O(n)。
结合前面,我们分析数组的时间复杂度时,采用均摊复杂度,则,在最坏情况下,出现扩容、缩容,其时间复杂度仍然是O(1)。
四 栈的应用
例如浏览器的前进后退操作,文本编辑器的撤销和前进操作,系统调用栈,函数调用栈,表达式求值的实现,表达式中特殊符号(括号)是否匹配等。
2条评论
Pingback:
Pingback: