相信大家平时在码代码的时候会经常出现StackOverflowError,这个错误就是栈溢出的错误了.而栈溢出经常是因为方法运行的时候,请求新建栈帧时
栈所剩空间小于战帧所需空间。
实例:在递归调用方法,不停产生栈帧,导致栈空间异常,抛出错误.(这时候我们应该要注意到我们递归出口否是定义了)
还有一点小知识进行拓展:
栈是一个线程私有的,通常用于保存方法(函数)中的参数,局部变量。在java中,所有基本类型和引用类型的引用都在栈中存储。栈中数据的生存空间一般在当前scopes内(就是由{…}括起来的区域)
但是,我们今天的重点不是这个.我们的重点是栈.我们接下来就认识一下栈,这么一个东西.
堆栈(也简称作栈)是一种特殊的线性表,堆栈的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置进行插入和删除操作,而堆栈只允许在固定一端进行插入和删除操作。
先进后出:堆栈中允许进行插入和删除操作的一端称为栈顶,另一端称为栈底。堆栈的插入和删除操作通常称为进栈或入栈,堆栈的删除操作通常称为出栈或退栈。(注意:这个是最大的特点:经常考试考研都会出到的)
还有以下这么几个概念:
栈:限定只在表尾进行插入和删除操作的线性表。
栈是线性表:其特殊性在于插入和删除操作必须在表尾。
空栈:栈中没有数据元素。
压栈(进栈):往栈中输入数据元素。
弹栈(出栈):从栈中输出数据元素。
栈顶:允许进行数据插入删除操作的一端是栈。
栈底:不允许进行数据插入删除操作的一端是栈。
共享栈:指有两个相同类型的顺序栈,一个栈已经满载,另一个却仍有空闲时间,为了能充分利用到未被使用的空闲空间,可以采用共享栈。
由于栈是线性表的特例,而线性表我们是采用数组实现的,而我们也用数组模拟顺序栈.
代码实现:
/**
* 思路:
* 1. 利用数组初始化栈对象
* 2. 对栈进行安全性判断
* 3. 入栈出栈操作
* @author Administrator
*
* @param <T>
*/
public class LinearStack<T> {
//初始栈的数据
private T[] data;
private int size;
private int top=-1;
//建长度为5的空栈
LinearStack() {
this(null);
}
//建长度为5,栈顶为element的栈
LinearStack(T element) {
this(element,5);
}
//1.初始化栈,自定义大小和参数对象传入
public LinearStack(T element, int size) {
if(size <= 0) {
return;
}
data = (T[]) new Object[size];
this.size = size;
if(element != null) {
data[0] = element;
top++;
}
}
//获取长度
public int getlength(){
if(top==-1){
return 0;
}else{
return top+1;
}
}
//判断是否为空
public boolean isEmpty(){
return top==-1;
}
public boolean isFull(){
return top==size-1;
}
//压栈
/**
* 思路:
* 1. 先++拓展出一个空位
* 2. 然后再赋值
* @param element
* @return
*/
public boolean Push(T element) {
if(isFull()) return false;
top++;
data[top] = element;
return true;
}
//弹栈
public T Pop() {
if(isEmpty()) return null;
T temp = data[top];
top--;
return temp;
}
//重写父类toString方法,做栈顶到栈底的遍历
public String toString() {
int len = getlength();
if(len == 0) {
return "[ ]";
}else{
//字符串拼接
StringBuffer sb = new StringBuffer();
sb.append("[ ");
for(int i = 0; i < len; i++) {
sb.append(data[i]+" ");
}
sb.append("]");
return sb.toString();
}
}
public static void main(String[] args) {
LinearStack<String> linearStack = new LinearStack<>("1");
//去掉初始化那个吧
linearStack.Pop();
linearStack.Push("A");
linearStack.Push("B");
linearStack.Push("C");
linearStack.Push("D");
linearStack.Push("E");
System.out.println(linearStack.getlength());
linearStack.Pop();
linearStack.Pop();
System.out.println(linearStack.getlength());
}
}
当然我们还有一个是链式栈,但是实际应用中,我们更多是讨论的顺序栈,对于链式栈,就是用链式表表达栈,每一个位置用一个结点表示.大家可以到时候google一下相关的.
两者优劣势:
顺序栈和链栈的插入、删除操作时间复杂度都是O(1);
顺序栈需要事先确定数组的长度,有可能存在浪费内存空间的情况;
链栈虽然不需要事先确定表长,但因为需要存储链式指针,同时加大了内存开销;
因此,如果数据元素变化不可预测,时大时小,最好使用链栈;如果它的变化空间在可控范围内,则可以考虑使用顺序栈。