前言
栈(stack)是一种线性数据的逻辑存储结构。栈中的元素只能先入后出(First In Last Out,简称FILO)。最早进入的元素存放的位置叫作栈底(bottom),最后进入的元素存放的位置叫作栈顶 (top)。
1. 概念
它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。如下图所示,向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。特点是先进后出。
2. 存储原理
栈是逻辑结构,底层存储结构,既可以用数组来实现,也可以用链表来实现。
2.1 数组实现
栈的数组实现如下,数组实现的栈也叫顺序栈或静态栈:
2.2 链表实现
栈的链表实现如下,链表实现的栈也叫做链式栈或动态栈:
3. 操作
下面代码演示以数组为底层存储结构为例,链表代码逻辑类似。
3.1 定义栈
package org.wanlong.stack;
/**
* @author wanlong
* @version 1.0
* @description: 数组实现的栈
* @date 2023/5/22 19:04
*/
public class ArrayStack {
//私有化数组,禁止外部直接访问,限制对数组的操作只能通过,提供的出栈,入栈操作实现
private Object[] nums; // 数组
private int count; // 栈中元素个数
// 初始化数组,申请一个大小为n的数组空间
public ArrayStack(int n) {
this.nums = new Object[n];
this.count = 0;
}
}
3.2 入栈
/**
* @Description:入栈操作
* @Author: wanlong
* @Date: 2023/5/22 19:08
* @param n: 入栈元素
* @return boolean
**/
public boolean push(Object n) {
// 数组空间不够了,直接返回false,入栈失败。 这里不扩容,后续可以完善代码实现扩容
if (count >= nums.length)
return false;
// 将item放到下标为count的位置,并且count加一
nums[count] = n;
count++;
return true;
}
3.3 出栈
/**
* @return Object 栈顶元素
* @Description:出栈操作
* @Author: wanlong
* @Date: 2023/5/22 19:07
**/
public Object pop() {
// 栈为空,则直接返回0
if (count == 0) return 0;
// 返回下标为count-1的数组元素,并且栈中元素个数count减一
Object n = nums[count - 1];
count--;
return n;
}
3.4 测试类
package org.wanlong.stack;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* @author wanlong
* @version 1.0
* @description: 测试栈的使用
* @date 2023/5/22 19:11
*/
public class Client {
static ArrayStack arrayStack = new ArrayStack(10);
@BeforeClass
public static void init() {
//入栈两个元素
arrayStack.push(1);
arrayStack.push(2);
arrayStack.push(23);
arrayStack.push("我是栈顶元素");
}
@Test
public void testPush() {
arrayStack.push(1);
arrayStack.push(2);
}
@Test
public void testPop() {
Object pop = arrayStack.pop();
System.out.println("栈顶出栈元素为:" + pop);
Object pop2 = arrayStack.pop();
System.out.println("出栈一个后的栈顶元素为:" + pop2);
}
}
3.5 运行结果
栈顶出栈元素为:我是栈顶元素
出栈一个后的栈顶元素为:23
4. 时间复杂度
- 入栈和出栈的时间复杂度都是O(1)
- 支持动态扩容的顺序栈,因为需要扩容,所以入栈的时间复杂度是O(n)
5.应用
5.1 函数调用
每进入一个函数,就会将临时变量作为一个栈入栈,当被调用函数执行完成,返回之后,将这个函数对应的栈帧出栈,我们在开发工具debug代码的典型应用。 如下图所示,即为栈帧。
5.2 浏览器的后退功能
我们使用两个栈,X 和 Y,我们把首次浏览的页面依次压入栈 X,当点击后退按钮时,再依次从栈X 中出栈,并将出栈的数据依次放入栈 Y。当我们点击前进按钮时,我们依次从栈 Y 中取出数据,放入栈 X 中。当栈 X 中没有数据时,那就说明没有页面可以继续后退浏览了。当栈 Y 中没有数据,那就说明没有页面可以点击前进按钮浏览了。
6.源码
java中LinkedList 是用双向链表实现的栈,源码在前面的链表章节提到过,感兴趣可以看前面的内容。
以上,本人菜鸟一枚,如有问题,请不吝指正。