栈的应用
在这里举几个比较常见的栈应用的例子
- 子过程的调用,每当在一个函数中调用另一个函数时,就会发生子过程的调用,而这个工程的实现就用到了栈,当程序执行到子函数所在的行时,会将此时的执行信息压入到系统栈中,当执行完子函数后,会将系统栈中的内容出栈,然后接着执行主函数,最常见的例子就是递归的调用。
- undo操作,也就是撤销操作,ctrl+z,每当我们在编译器输入词汇的时候,编译器会将我们输入的内容压入栈中,栈顶记录着我们最近输入的元素,当我们输错时,想要撤销,那么此时就会将栈顶的元素出栈,从而达到撤销的效果
- 括号匹配问题,比如我们在用Eclipse时,if,while等,都需要加上括号,当我们在进行嵌套循环时,编译器就需要识别那个括号和那个括号匹配,这是就需要用到栈,关于括号匹配问题详见:190421打卡:有效括号匹配
操作
public interface Stack<E> {
int getSize(); //元素个数
boolean isEmpty(); //是否为空
void push(E e); //入栈
E pop(); //返回出栈元素
E peek(); //返回栈顶元素
}
栈的复杂度分析
其中push和pop操作涉及数组的扩容和缩容,均摊复杂的为O(1)
源码实现
该栈的实现支持扩容和缩容,以下源码中数组Array是引用:
算法与数据结构之动态数组的实现Java版
为了代码结构上的完整性,在上述数组的基础上,添加了两个方法
//获取最后一个位置的元素
public E getLast() {
return get(size - 1); //注意这里为什么不写成return data[size - 1],因为这样写缺少对数组下标的判断
}
//获取第一个位置的元素
public E getFirst() {
return get(0);
}
以下为栈实现的源码
package stack;
public class ArrayStack<E> implements Stack<E> {
private Array<E> array;
public ArrayStack(int capacity) {
array = new Array<>(capacity);
}
public ArrayStack() {
array = new Array<>();
}
@Override
public int getSize() {
return array.getSize();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
public int getCapacity() {
return array.getCapacity();
}
@Override
public void push(E e) {
array.addLast(e);
}
@Override
public E pop() {
return array.removeLast();
}
@Override
public E peek() {
return array.getLast();
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("Stack:");
res.append("[");
for(int i = 0; i < array.getSize(); i++) {
res.append(array.get(i));
if (i != array.getSize() - 1) {
res.append(", ");
}
}
res.append("] top");
return res.toString();
}
}