第三章 栈、队列和数组相关结构

3.1 栈

3.1.1 栈的定义

**栈(Stack)**是一种只允许在一端进行插入和删除操作的线性表。
允许操作的一端称为栈顶,另一端称为栈底。

栈遵循:

后进先出(LIFO,Last In First Out)。

3.1.2 栈的基本操作

操作 含义
push 入栈,将元素放入栈顶
pop 出栈,移除并返回栈顶元素
peek 获取栈顶元素但不删除
isEmpty 判断栈是否为空
size 获取栈中元素数量

3.1.3 栈的顺序存储

  • 使用数组保存栈元素,并通过栈顶下标管理入栈、出栈操作。
java 复制代码
class ArrayStack {
    private final int[] data;
    private int top = -1;

    public ArrayStack(int capacity) {
        data = new int[capacity];
    }

    public boolean isEmpty() {
        return top == -1;
    }

    public boolean isFull() {
        return top == data.length - 1;
    }

    public void push(int value) {
        if (isFull()) {
            throw new IllegalStateException("栈已满");
        }
        data[++top] = value;
    }

    public int pop() {
        if (isEmpty()) {
            throw new IllegalStateException("栈为空");
        }
        return data[top--];
    }

    public int peek() {
        if (isEmpty()) {
            throw new IllegalStateException("栈为空");
        }
        return data[top];
    }
}

其中:

  • top = -1 表示栈为空。
  • 入栈前先将 top 加一。
  • 出栈时先读取栈顶元素,再将 top 减一。

3.1.4 栈的链式存储

链栈使用链表结点保存元素,通常在链表头部进行入栈和出栈。
特点:

  1. 不需要预先固定数组容量。
  2. 入栈和出栈通常为 (O(1))。
  3. 需要额外存储指针。
  4. 仍可能受到可用内存限制。

3.1.5 栈的应用

  • 函数调用与递归。
  • 括号匹配。
java 复制代码
- 表达式求值。
- 深度优先搜索。
- 撤销操作。
- 浏览器历史记录中的部分回退机制。

本节总结: 栈的核心是后进先出。入栈和出栈通常都可以在 (O(1)) 时间内完成,常用于需要逆序处理或保存执行状态的场景。


3.2 队列

3.2.1 队列的定义

**队列(Queue)**是一种只允许在一端插入、另一端删除的线性表。

  • 队尾:插入元素的一端。
  • 队头:删除元素的一端。

队列遵循:

先进先出(FIFO,First In First Out)。

3.2.2 队列的基本操作

操作 含义
offer / enqueue 入队
poll / dequeue 出队
peek 查看队头元素
isEmpty 判断队列是否为空
size 获取队列元素数量

具体返回值和异常行为取决于所使用的队列实现。

3.2.3 队列的顺序存储

普通顺序队列可以使用数组保存元素,并维护队头、队尾位置。
若仅使用不断递增的数组下标,出队后前面的空间可能无法直接复用,容易造成假溢出。

3.2.4 循环队列

循环队列将数组的首尾逻辑连接,使队列能够循环使用数组空间。
若数组长度为 capacity,常用的下标更新方法为:

java 复制代码
rear = (rear + 1) % capacity;
front = (front + 1) % capacity;

取模操作可以让下标到达数组末尾后重新回到开头。
一种常见实现方式是预留一个空位置:

  • 队列为空:front == rear
  • 队列为满:(rear + 1) % capacity == front
java 复制代码
此时实际可存储的最大元素数为 `capacity - 1`。

3.2.5 队列的链式存储

链队列通过链表存储元素,通常设置队头指针和队尾指针。

  • 入队:在队尾添加结点。
复制代码
- 出队:删除队头结点。
- 空队列时,需要同时维护队头和队尾指针。
  • 在实现正确的情况下,链队列的入队和出队通常都可以在 (O(1)) 时间内完成。

3.2.6 队列的应用

  • 操作系统任务调度。
  • 打印任务排队。
复制代码
- 广度优先搜索。
- 消息队列。
- 缓冲区管理。
- 按到达顺序处理请求。

本节总结: 队列遵循先进先出。循环队列用于复用数组空间,链队列通过指针连接结点,两者都可以高效实现入队和出队。


3.3 栈与队列的比较

比较项目 栈 队列
访问原则 后进先出 先进先出
插入位置 栈顶 队尾
删除位置 栈顶 队头
主要操作 入栈、出栈 入队、出队
常见实现 数组、链表 循环数组、链表
常见应用 递归、表达式求值、括号匹配 任务调度、层序遍历、消息处理

本节总结: 栈和队列都属于受限线性表,区别在于插入和删除的位置不同,因此形成了不同的数据处理顺序。


3.4 双端队列

3.4.1 定义

双端队列(Deque) 允许在队列的两端进行插入和删除操作。

常见操作包括:

  • 队头插入。
复制代码
- 队尾插入。
- 队头删除。
- 队尾删除。

3.4.2 Java 中的双端队列

Java 中常用 Deque 接口表示双端队列,ArrayDeque 是常见实现类。

java 复制代码
import java.util.ArrayDeque;
import java.util.Deque;

public class DequeDemo {
    public static void main(String[] args) {
        Deque<Integer> deque = new ArrayDeque<>();

        deque.offerFirst(10);
        deque.offerLast(20);

        System.out.println(deque.peekFirst());
        System.out.println(deque.peekLast());

        deque.pollFirst();
        deque.pollLast();
    }
}

常用方法:

方法 含义
offerFirst(e) 在队头插入
offerLast(e) 在队尾插入
pollFirst() 删除并返回队头元素
pollLast() 删除并返回队尾元素
peekFirst() 查看队头元素
peekLast() 查看队尾元素

ArrayDeque 不允许存储 null 元素。

3.4.3 双端队列的应用

  • 实现栈和队列。
  • 滑动窗口问题。
  • 双向调度。
  • 需要在两端灵活操作的数据处理场景。

本节总结: 双端队列兼具两端插入和删除的能力。在 Java 中,Deque 是接口,ArrayDeque 是常用实现类。


3.5 栈在表达式求值中的应用

3.5.1 常见表达式形式

表达式类型 特点 示例
中缀表达式 运算符位于操作数之间 A + B
前缀表达式 运算符位于操作数之前 + A B
后缀表达式 运算符位于操作数之后 A B +

3.5.2 运算符优先级

常见运算符优先级从高到低:

  1. 括号中的表达式。
  2. 乘法、除法、取余。
  3. 加法、减法。
  • 同一优先级下的运算通常按照运算符结合性处理。具体表达式还需要考虑括号、单目运算符等规则。

3.5.3 后缀表达式求值

  • 后缀表达式可以借助栈计算。

  • 基本步骤:

  1. 从左向右扫描表达式。
  2. 遇到操作数时,将操作数入栈。
  3. 遇到二元运算符时,先弹出右操作数,再弹出左操作数。
  4. 执行运算,并将结果重新入栈。
  5. 扫描结束后,栈顶元素为结果。

例如:

java 复制代码
3 4 + 2 *

-计算过程:

  1. 3 入栈。
  2. 4 入栈。
  3. 遇到 +,计算 3 + 4 = 7。
  4. 7 入栈。
  5. 2 入栈。
  6. 遇到 *,计算 7 * 2 = 14。

最终结果:

java 复制代码
14 

本节总结: 栈可以保存尚未完成运算的操作数和运算符。后缀表达式求值时,二元运算符需要先取右操作数,再取左操作数。


3.6 数组与特殊矩阵的存储

3.6.1 数组的基本概念

1.数组由相同类型的元素组成,元素通过下标访问。

2.在顺序存储的情况下,数组元素通常占据连续的存储空间。

3.二维数组可以看作由多个一维数组组成的逻辑结构,其实际存储顺序由具体语言和实现决定。

3.6.2 特殊矩阵

特殊矩阵是具有特定规律的矩阵,例如:

  • 对称矩阵。
  • 上三角矩阵。
  • 下三角矩阵。
  • 对角矩阵。
    当矩阵中的大量元素具有固定值或可由其他元素推导时,可以只存储必要的数据,以节省空间。

3.6.3 对称矩阵

对称矩阵满足:

java 复制代码
a{i,j}=a{j,i}  

因此,主对角线两侧的元素可以相互对应,只存储其中一侧及主对角线元素即可恢复整个矩阵。

3.6.4 稀疏矩阵

稀疏矩阵是非零元素数量远少于矩阵元素总数的矩阵。
常见存储方式:

  1. 三元组表:保存行号、列号和值。
  2. 十字链表:通过链表组织非零元素及其行列关系。
  3. 压缩稀疏行或列存储:常用于科学计算和数值分析。
  • 三元组的基本结构:
行号 列号 元素值
(i) (j) (a_{ij})
  • 仅保存非零元素及其位置,可以减少稀疏矩阵的存储开销。

本节总结: 数组支持按下标访问,特殊矩阵可以利用数学规律压缩存储,稀疏矩阵则通过只保存非零元素减少空间占用。


第三章重点知识汇总

知识点 核心内容
栈 后进先出,主要操作是入栈和出栈
队列 先进先出,主要操作是入队和出队
循环队列 利用取模操作循环使用数组空间
链队列 通过链表连接元素,维护队头和队尾
双端队列 支持在队头、队尾插入和删除
表达式求值 使用栈管理操作数及运算过程
数组 使用下标访问元素
对称矩阵 利用 (a*{ij}=a*{ji}) 压缩存储
稀疏矩阵 重点存储非零元素及其位置信息
相关推荐
瑶山1 小时前
stable-diffusion.cpp 参数全集整理(sd-cli.exe / sd-server.exe)
java·数据库·stable diffusion·参数全集
CallFay云起未来1 小时前
2026年AI客服能不能减少人工回复?从知识检索到人机协同的技术实现
java·大数据·前端·人工智能·架构·文心一言
伯恩bourne1 小时前
对比 @Configuration(Full) vs @Component(Lite)
java·开发语言
RuoyiOffice1 小时前
达梦 DM8、openGauss、人大金仓怎么选?RuoYi Office 国产数据库适配与验收指南
java·数据库·spring boot·架构·信创
修船大队长1 小时前
KL15 高电平请求 FullCom:AUTOSAR 项目调用流程
android·java·服务器
棉猴1 小时前
玩游戏学Python12-布尔型与条件语句
java·前端·python·玩游戏·游戏编程·pgzero
java_nnnn2 小时前
Spring Web MVC 入门:从一个 URL 到 Java 后端完整处理请求
java·前端·spring boot·spring
xys_6782 小时前
深挖数据结构:线性表(1)
数据结构
Mikko72 小时前
fastjson 1.2.84 能在 JDK 7 上跑吗?实测:编译能过,一启动就 UnsupportedClassVersionError
java·后端·jdk·fastjson·安全漏洞