栈的抽象数据类型
栈是一种用于存储数据的简单数据结构,有点类似链表或者顺序表(统称线性表),栈与线性表的最大区别是数据的存取的操作,我们可以这样认为栈(Stack)是一种特殊的线性表,其插入和删除操作只允许在线性表的一端进行,一般而言,把允许操作的一端称为栈顶(Top),不可操作的一端称为栈底(Bottom),同时把插入元素的操作称为入栈(Push),删除元素的操作称为出栈(Pop)。若栈中没有任何元素,则称为空栈。
栈只能从栈顶存取元素,同时先进入的元素反而是后出,而栈顶永远指向栈内最顶部的元素。到此可以给出栈的正式定义:栈(Stack)是一种有序特殊的线性表,只能在表的一端(称为栈顶,top,总是指向栈顶元素)执行插入和删除操作,最后插入的元素将第一个被删除,因此栈也称为后进先出(Last In First Out,LIFO)或先进后出(First In Last Out FILO)的线性表。栈的基本操作创建栈,判空,入栈,出栈,获取栈顶元素等,注意栈不支持对指定位置进行删除,插入,其接口Stack声明如下:
public interface Stack<T> {
/**
* 栈是否为空
* @return
*/
boolean isEmpty();
/**
* data元素入栈
* @param data
*/
void push(T data);
/**
* 返回栈顶元素,未出栈
* @return
*/
T peek();
/**
* 出栈,返回栈顶元素,同时从栈中移除该元素
* @return
*/
T pop();
}
##### 顺序栈的设计与实现:
顺序栈的内部以顺序表为基础,实现对元素的存取操作,当然我们还可以采用内部数组实现顺序栈,在这里我们使用内部数据组来实现栈,至于以顺序表作为基础的栈实现,将以源码提供:
```java
package com.lzh.stack;
import java.io.Serializable;
import java.util.EmptyStackException;
import java.util.Stack;
public class StackTest<T> extends Stack<T> implements Serializable {
private static final long serialVersionUID=-5413303117698554397L;
/*
*栈顶指针,-1代表空栈
*/
private int top=-1;
/*
容量大小默认为10
*/
private int capacity=10;
/*
存放元素的数组
*/
private T[] array;
private int size;
public StackTest(int capacity){
array=(T[]) new Object[capacity];
}
public StackTest(){
array=(T[])new Object[this.capacity];
}
/*
获取栈顶元素值,不删除
*/
@Override
public synchronized T peek() {
if (isEmpty()){
new EmptyStackException();
}
return array[top];
}
/*
添加元素,从栈顶(数组尾部)插入
容量不足时,需要扩容
*/
@Override
public T push(T data) {
//判断容量是否充足
if (array.length == size){
ensureCapacity(size*2 +1);
}
//从栈顶添加元素
array[++top]=data;
size++;
return data;
}
/*
从栈顶(顺序表尾部)删除
*/
@Override
public synchronized T pop() {
if (isEmpty()){
new EmptyStackException();
}
size--;
return array[top--];
}
@Override
public synchronized boolean isEmpty() {
return this.top==-1;
}
@Override
public int size() {
return size;
}
/*
扩容的方法
*/
@Override
public void ensureCapacity(int capacity) {
if (capacity < size){
return;
}
T[] old=array;
array=(T[])new Object[capacity];
//复制元素
for (int i=0;i<size;i++){
array[i]=old[i];
}
}
public static void main(String[] args) {
StackTest<String> s=new StackTest<String>();
//压入栈
s.push("A");
s.push("B");
s.push("C");
System.out.println("size ->"+s.size);
int l=s.size(); //size在减少,必须先记录
for (int i=0;i<l;i++){
//推出栈
System.out.println("s.pop->"+s.pop());
System.out.println("s.peek->"+s.peek());
}
//输出栈顶
System.out.println("s.peek->"+s.peek());
}
}
链式栈(Linked Stack),就是采用链式存储结构的栈,由于我们操作的是栈顶一端,因此这里采用单链表(不带头结点)作为基础,直接实现栈的添加,获取,删除等主要操作即可。
顺序栈与链式栈中各个操作的算法复杂度(时间和空间)对比,顺序栈复杂度如下:
操作 时间复杂度
SeqStack空间复杂度(用于N次push) O(n)
push()时间复杂度 O(1)
pop()时间复杂度 O(1)
peek()时间复杂度 O(1)
isEmpty()时间复杂度 O(1)
链式栈复杂度如下:
操作 时间复杂度
SeqStack空间复杂度创建(用于N次push) O(n)
push()时间复杂度 O(1)
pop()时间复杂度 O(1)
peek()时间复杂度 O(1)
isEmpty()时间复杂度 O(1)
由此可知栈的主要操作都可以在常数时间内完成,这主要是因为栈只对一端进行操作,而且操作的只是栈顶元素。