前面已经提及了多次,栈是一种操作受限的线性表,其操作的规则是后进先出(Last In First Out),栈的主要操作有进栈(PUSH)、出栈(POP),主要应用有表达式求值(下篇博客会详细剖析前缀、中缀、后缀表达式与栈的应用)、消除递归、深度优先搜索等。
抽象数据类型
这回开始用Java来编写了,应该更容易看懂了。
package top.zhanglugao.stack;
/***
* 栈的抽象数据类型
* @author zhanglugao
*/
public interface Stack<T> {
void clear(); //清空栈
boolean push(T t); //元素入栈
T pop(); //获取栈顶元素并弹出
T top(); //获取栈顶元素不弹出
boolean isEmpty(); //判断栈是否为空栈
boolean isFull(); //判断栈是否已满
}
实现方式
1.顺序表实现栈-顺序栈(Array Based Stack)
使用向量实现,本质上是顺序表的简化版,需要确定哪一端作为栈顶。
1⃣️ 类定义:
package top.zhanglugao.stack;
public class ArrayStack<T> implements Stack<T>{
private int mSize; //栈存放元素的最大值
private int top; //栈顶元素位置
T[] st; //实际存放元素的数组
public ArrayStack(int size){
st= (T[]) new Object[size]; //初始化数据
}
/***
* 清空栈
*/
public void clear() {
st=(T[]) new Object[mSize];
top=-1;
}
/***
* 获得栈顶元素但不弹出
* @return
*/
public T top() {
return st[top];
}
public boolean isEmpty() {
if(top==-1)return true;
return false;
}
public boolean isFull() {
if(top==mSize-1)return true;
return false;
}
}
2⃣️ 顺序栈的溢出
上溢:当栈中元素已满,继续往栈中添加元素时发生的现象。
下溢:对空栈执行出栈操作时产生的现象
3⃣️ 入栈(PUSH)与出栈(POP)方法的详细实现
/***
* 元素入栈
* @param t 要入栈的元素
* @returnt
*/
public boolean push( T t ) {
//先判断栈是否已满 已满的标识是top=mSize-1
if(top==mSize-1){
System.out.println("栈已满,无法继续添加");
return false;
}
//进行添加 处理过程是把t存入数组top+1的位置 再将top值加1
st[top+1]=t;
top++;
return true;
}
/**
* 获得栈顶元素并弹出
* @return
*/
public T pop() {
if(top==-1){
System.out.println("栈空,无法获取");
return null;
}
//栈顶元素为st[top] 取出来后将st[top]置为null 再将top的值-1
T t=st[top];
st[top]=null;
top--;
return t;
}
2.单链表实现栈-链式栈(Linked Stack)
用单链表方式存储,其中指针的方向是由栈顶向下链接。
1⃣️ 类定义:
package top.zhanglugao.stack;
/***
* 链式表实现栈 因为java中没有指针 Link类直接记录下个元素的内容
* @param <T>
*/
public class LinkedStack<T> implements Stack<T> {
private int size; //栈内元素数目
private Link<T> top; //栈顶元素
public void clear() {
size=0;
top=null;
}
/**
* 获取栈顶元素不弹出
* @return
*/
public T top() {
return top.getData();
}
/***
* size=0的情况下 栈为空
* @return 栈是否为空
*/
public boolean isEmpty() {
return size==0;
}
/**
* 单链表组成的栈 可以不用关注栈是否满
* @return false
*/
public boolean isFull() {
return false;
}
public static void main(String[] args) {
LinkedStack<Integer> linkedStack=new LinkedStack<Integer>();
linkedStack.push(1);
linkedStack.push(2);
linkedStack.push(3);
linkedStack.push(4);
int res=linkedStack.top();
System.out.println("栈顶元素为:"+res);
int res2=linkedStack.pop();
System.out.println("栈顶元素为:"+res2);
System.out.println(linkedStack.top());
}
}
class Link<T> {
private T data;
private Link<T> next;
public Link(T data,Link<T> next){
this.data=data;
this.next=next;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Link<T> getNext() {
return next;
}
public void setNext(Link<T> next) {
this.next = next;
}
}
2⃣️ 入栈与出栈方法的实现
/***
* 将元素t压入栈顶
* @param t
* @return
*/
public boolean push(T t) {
//新建Link<T> next域链接的是当期top 再将top指向新建的Link<T>
Link<T> newElement=new Link<T>(t,top);
top=newElement;
size++;
return true;
}
/**
* 获取栈顶元素并弹出
* @return
*/
public T pop() {
if(size==0){
System.out.println("栈空,无法获取");
return null;
}
//获取栈顶元素后,将top指向top的next节点 size减一
T data=top.getData();
top=top.getNext();
size--;
return data;
}
3.顺序栈与链式栈的比较
时间效率:所有操作都只需要常数时间,时间复杂度为O(1),在时间效率上顺序栈与链式栈难分伯仲
空间效率:顺序栈需要说明一个固定的长度,链式栈的长度可变,但增加了指针域的结构性开销
在实际应用中,顺序栈比链式栈用的更广泛,顺序栈更容易读取栈定位置,进行相对位移,快速定位并读取栈的内部元素。
顺序栈读取内部元素的时间为O(1),链式栈因为要沿着指针链游走,时间为(n),虽然一般来说,栈是不允许读取内部元素的。