栈(Stack)
栈的简介
栈是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作,栈的重要特点为:后进先出或先进后出。
例如:
栈的一个最重要的特征就是栈的插入和删除只能在栈顶进行,所以每次删除的元素都是最后进栈的元素,故栈也被称为后进先出(Last In First Out)的线性表,简称LIFO表。
Deque 的体系关系:
Java中有一个类Stack,用于表示栈,但这个类已经过时了。Java中没有单独的栈接口,栈相关方法包括在了表示双端队列的接口Deque中,主要有四个方法:
| 方法名 | 描述 |
|---|---|
| void push(E e); | 入栈,在头部添加元素,栈的空间可能是有限的,如果栈满了,会抛出异常。 |
| E pop(); | 出栈,返回头部元素,并且从栈中删除,如果栈为空,会抛出异常。 |
| E peek(); | 查看栈头部元素,不修改栈,如果栈为空,返回null。 |
| boolean isEmpty(); | 判断栈是否为空,如果为空则返回true;否则返回false。 |
【示例】使用LinkedList实现栈操作
/*
@Author xiangge
@Date 2023/7/19
@Description 栈结构
void push(E e);入栈,在头部添加元素,栈的空间可能是有限的,如果栈满了,会抛出异常。
E pop();出栈,返回头部元素,并且从栈中删除,如果栈为空,会抛出异常。
E peek();查看栈头部元素,不修改栈,如果栈为空,返回null。
boolean isEmpty();判断栈是否为空,如果为空则返回true;否则返回false。
*/
public class DequeDemo1 {
public static void main(String[] args) {
Deque<Integer> deque = new LinkedList<>();
deque.push(11);
deque.push(22);
deque.push(33);
deque.push(44);
deque.push(55);
System.out.println(deque);//[55, 44, 33, 22, 11]
while (!deque.isEmpty()){
System.out.println(deque.pop());
}
System.out.println(deque);
}
}
栈的实现方式
每个栈都有一个栈顶指针,它初始值为-1,且总是指向最后一个入栈的元素,栈有两种处理方式,即进栈(push)和出栈(pop),因为在进栈只需要移动一个变量存储空间,所以它的时间复杂度为O(1),但是对于出栈分两种情况,栈未满时,时间复杂度也为O(1),但是当栈满时,需要重新分配内存,并移动栈内所有数据,所以此时的时间复杂度为O(n)。以下举例栈结构的两种实现方式,顺序表存储和链表存储。
数组模拟栈实现
要求:需要限制栈的最大容量。
package com.huayu.stack;
/**
* 数组模拟栈(限制最大容量)
*/
public class ArrayStack<T> {
/**
* 存放数据的容器
*/
private Object[] elementData;
/**
* 指向栈顶的指针
*/
private int top = -1; // 默认值为-1
/**
* 保存栈的最大容量
*/
private int maxSize;
/**
* 构造方法
* @param maxSize 设置栈的最大容量
*/
public ArrayStack(int maxSize) {
this.elementData = new Object[maxSize];
this.maxSize = maxSize;
}
/**
* 入栈操作
* @param value
*/
public void push(T element) {
// 1.判断栈是否已满
if(isFull()) {
throw new RuntimeException("栈已满,无法执行入栈操作");
}
// 2.添加数据入栈
elementData[++top] = element;
}
/**
* 出栈操作
*/
public T pop() {
// 1.判断栈是否为空
if(isEmpty()) {
throw new RuntimeException("栈为空,无执行出栈操作");
}
// 2.获取需要出栈的元素
T value = (T)elementData[top];
// 3.清空栈顶存放的数据
elementData[top] = null;
// 4.修改栈顶指针的指向
top--;
// 6.返回需要取出的栈顶元素
return value;
}
/**
* 检查栈是否为空
* @return
*/
public boolean isEmpty() {
return top == -1;
}
/**
* 判断栈是否已满
* @return
*/
private boolean isFull() {
return maxSize - 1 == top;
}
}
链表模拟栈实现
要求:无需限制链表的最大容量。
package com.huayu.p3.demo;
/**
* 链表模拟栈(不限制最大容量)
*/
public class LinkedListStack<T> {
/**
* 保存单链表首节点
*/
private StackNode<T> topNode;
/**
* 保存实际添加元素的个数
*/
private int size;
/**
* 入栈操作(链表的头部开始插入)
* @param element 需要入栈的数据
*/
public void push(T element) {
// 1.把element元素封装成节点对象
StackNode<E> node = new StackNode<>(element);
// 2.把node节点添加进入链表(把node插入到topNode的前面)
// 把node节点的next指向topNode节点
node.next = topNode;
// 更新topNode指向node节点
topNode = node;
// 3.更新size的值,也就是执行size++操作
size++;
}
/**
* 出栈操作(删除单链表首节点)
* @return 返回栈顶的数据
*/
public T pop() {
// 判断栈是否为空
if(isEmpty()) {
throw new RuntimeException("栈为空,无执行出栈操作");
}
// 定义tempNode变量,来零时保存headNode节点
StackNode<T> tempNode = topNode;
// 更新topNode的值,将topNode.next 改为:topNode
topNode = topNode.next;
// 完成出栈操作后,size递减
size--;
// 返回栈顶的数据
return tempNode.data;
}
/**
* 获取栈顶元素(不会删除栈顶元素)
* @return
*/
public T peek() {
// 判断栈是否为空
if (isEmpty()) {
return null;
}
// 返回栈顶数据
return topNode.data;
}
/**
* 判断栈是否为空
* @return 如果栈为空,则返回true;如果栈非空,则返回false。
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 栈节点类
*/
private static class StackNode<T> {
/**
* 节点中存放的数据
*/
private T data;
/**
* 指向下一个节点
*/
private StackNode<T> next;
/**
* 构造方法
* @param data
*/
public StackNode(T data, StackNode<T> next) {
this.data = data;
this.next = next;
}
}
}