1. 栈(Stack)
1.1 文字讲解
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则(竖立的水杯模型)。其中一下两个操作是栈的主要操作:
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据在栈顶。
1.2 图像分析
从上图中可以看到,Stack继承了Vector父类,Vector和ArrayList类似,都是动态的顺序表,不同的是Vector是线程安全的。
下图图一和图二分别是入栈和出栈操作关于栈顶和栈底的图解:
2、自定义栈的实现
2.1 栈的实现方法和功能表达
详细方法和功能如下图:
2.2 自定义栈的代码实现
说明:本栈的实现主要 采用int型数组的结构;
2.2.1 栈的创建
思路:
1、创建一个MyStack类,其中定义两个成员变量,数组(用来存放底层数据)和长度(用来记录当前的栈里面存放数据的数量),实现3中定义的接口,重写其方法让栈的操作具体化;
2、构造方法,创建一个默认长度为10的数组;
public class MyStack implements IStack{
private int[] data;
private int usedLength;//实际已经占用的数组长度
private static final int Default_Length = 10;
public MyStack() {
this.data = new int[Default_Length];
}
@Override
public void push(int x) {
}
@Override
public int pop() {
return 0;
}
@Override
public int peek() {
return 0;
}
@Override
public int size() {
return 0;
}
@Override
public boolean empty() {
return false;
}
@Override
public boolean full() {
return false;
}
}
3、定义一个接口,在里面创建栈一系列操作的抽象方法;
public interface IStack {
void push(int x);
int pop();
int peek();
int size();
boolean empty();
boolean full();
}
2.2.2 压栈方法的实现
1、首先对当前栈进行判断是否为满,若满则需要对当前栈进行二倍容量扩容
2、当栈的容量充足时,在栈顶进行入栈(底层数组尾部进行添加数据)
3、当前栈的数量长度加一;
@Override
public void push(int x) {
if (full() == true){
this.data = Arrays.copyOf(data,2*Default_Length);
}
data[usedLength] = x;
usedLength++;
}
@Override
public boolean full() {
if (usedLength == data.length){
return true;
}
return false;
}
2.2.3 栈是否为空操作
@Override
public boolean empty() {
return usedLength == 0;
}
2.2.4出栈的实现
思路:
1、首先得判断当前栈栈是否为空,若为空我们需要抛出栈为空自定义异常(自定义一个异常为EmptyException);
public class EmptyException extends RuntimeException{
public EmptyException(String msg) {
super(msg);
}
}
2、将当前的栈顶的元素存储到容器中并返回
3、将栈的长度加一,之前栈顶的数据引用地址就会变成null(空指针),出栈的数据的地址就不会被引用而导致数据从栈里消失;
图解如下:
代码如下:
@Override
public int pop() {
if (empty()){
throw new EmptyException("当前这个栈是空的!!!");
}
int old = data[usedLength-1];
usedLength--;//相当于是删除
return old;
}
2.2.5获取栈中数据元素个数
@Override
public int size() {
return usedLength;
}
2.2.6获取栈顶元素
思路类似于2.2.4;
@Override
public int pop() {
if (empty()){
throw new EmptyException("当前这个栈是空的!!!");
}
int old = data[usedLength-1];
usedLength--;//相当于是删除
return old;
}
3.关于链表的栈使用
3.1单链表
从头部插入(入栈),从头部删除(出栈),时间复杂度为O(1);
从尾部插入(入栈),从尾部删除(出栈),时间复杂度为O(n);不建议使用
3.2双向链表:
从头部插入(入栈),从头部删除(出栈)
从尾部插入(入栈),从尾部删除(出栈),有last引用,直接到表尾部
二者时间复杂度皆为O(1);
3.3 LinkedList自带实现方法:
public static void main(String[] args) {
LinkedList<Integer> linkedList = new LinkedList<>();
linkedList.push(101);
linkedList.push(102);
linkedList.push(103);
linkedList.push(104);
System.out.println(linkedList.peek());
System.out.println(linkedList.pop());
System.out.println(linkedList.pop());
}
测试结果:
4.Q&A
Q1:栈、虚拟机栈、栈帧有什么区别呢?
A1:
栈:是一种数据结构里面的概念,用来存放数据的一种模式;
虚拟机栈:jvm虚拟机划分的一块内存;
栈帧:调用方法的时候会在虚拟机中给这个方法开辟一个空间;
ps:本次内容就到这里了,如果喜欢的话就请一键三连哦!!!