栈的定义
栈是只允许许一段进行插入或删除操作的线性表。首先栈是一种线性表,但限定这种线性表只能在某一段进行插入和删除操作,如下图所示
- 栈顶:线性表允许进入插入删除的那一端
- 栈底:固定的,不允许进行插入和删除的另一端
- 空栈:不含任何元素的空表
栈是一种受限的线性表,类似线性表,他也有对应的两种存储方式
栈分为顺序栈,共享栈,链栈
顺序栈的基本操作
初始化一个栈
public class MyStack { private int[] stack;//存放数据 private int top;//栈顶指针 private int maxSize;//数组元素最大个数 public MyStack(int maxSize) { this.maxSize = maxSize; stack = new int[maxSize]; top = -1;//栈顶指针初始化为-1 } }
栈中没有元素时,栈顶指针默认是-1
用构造方法指定栈的容量
判断栈为空
public boolean isEmpty(){ return top==-1; }
仅仅需要判断top指针
出栈
//出栈操作 public int pop(){ if(top==-1){ System.out.println("栈空"); return -1; }else{ int data = stack[top]; top--; return data; } }
进栈
//入栈操作 public void push(int data){ if(top==maxSize-1){ System.out.println("栈满"); }else{ top++; stack[top]=data; } }
读取栈顶元素
public Integer readTop(){ if(top==-1){ System.out.println("栈空"); return null; }else{ return stack[top]; } }
为什么用Integer类型?而不用int,原因是int类型返回值不能为空,而包装类Integer可以
输出栈中元素
//输出栈中元素 public void printStack(){ for(int i=top;i>=0;i--){ System.out.println(stack[i]); } }
共享栈定义
利用栈底卫生纸相对不变的特性,可让两个是顺序栈共享一个一维数组空间,将两个栈的栈底分别设置在共享空间的两端,两个栈顶想共享空间的中间延伸,如下如所示
两个栈的栈顶指针都指向栈顶元素,top0=-1时0号栈为空,top1=Maxsize时1号栈为空;仅当两个栈顶指针相邻(top1-top0=1)时,判断为栈满。当0号栈进栈时 top0 先加 1再赋值,1号栈进栈时top1先减1再赋值;出栈时则刚好相反。
共享栈是为了更有效地利用存储空间,两个栈的空间相互调节,只有在整个存储空间被占满时才发生上溢。其存取数据的时间复杂度均为O(1),所以对存取效率没有什么影响。
栈的链式存储
采用链式存储的栈称为链栈,链栈的优点是便于多个栈共享存储空间和提高其效率,且不存在栈满上溢的情况。通常采用单链表实现,并规定所有操作都是在单链表的表头进行的。值为a1的节点为栈顶节点
链栈的基本操作单链表的基本操作基本大同小异,这里就不过多介绍了
下面我们介绍一下Java自己的栈
首先需要导包 util包下的Stack,下图是相应的方法
- empty(): 这个方法通常用于检查栈是否为空。
- peek(): 返回栈顶的元素但不移除它。
- pop(): 移除并返回栈顶的元素。
- push(element): 将指定的元素压入栈顶。
- search(element): 在栈中搜索指定的元素并返回其位置,其中最顶部的元素位置为1。
我们可以看到,Stack继承Vector Vector相信大家不陌生,也是一个动态数组,和ArrayList有点相似,不过Vector是线程安全的
下面我们来详解一下Stack进栈的过程
//下面的代码为Stcak进栈的方法,可以看到,它调用了vector的addElement方法 public E push(E item) { addElement(item); return item; } //我们接着点进去看看 public synchronized void addElement(E obj) { modCount++; add(obj, elementData, elementCount); } //首先我们看modCount,这个属性并不是vector类的,而是vector父类AbstractList<>类中的属性,此字段由 iterator 和 listIterator 方法返回的 iterator 和 list iterator 实现使用。如果此字段的值意外更改,则迭代器(或列表迭代器)将抛出 ConcurrentModificationException 以响应 next、remove、previous、set 或 add 操作。 //接着我们看add方法 此方法是本类的 //此方法就是将e添加到elementData数组中s的位置,很好理解 private void add(E e, Object[] elementData, int s) { if (s == elementData.length) //如果数组满了就对其扩容 elementData = grow(); elementData[s] = e; elementCount = s + 1; }