链表与数组
数组是连续的内存空间
链表是非连续的内存空间,链表的每个元素都存储了下一个元素的地址,从而使一系列随机的内存地址串在一起。
在链表中添加元素需要将元素放入内存,并将其地址存储到前一个元素中。
数组支持随机访问某个元素,但是链表不能,想要访问最后一个元素,必须从头开始访问
需要在中间插入元素时,链表是更好的选择。
如果你要删除元素呢?链表也是更好的选择,因为只需修改前一个元素指向的地址即可。而
使用数组时,删除元素后,必须将后面的元素都向前移
栈(Stack)是一种遵循后进先出(LIFO, Last In First Out)原则的线性数据结构,其核心特性体现在操作规则、存储方式和应用场景上。以下是栈的详细特性:
1. 后进先出(LIFO)原则
- 定义:最后入栈的元素会最先出栈,类似于叠放的书本或盘子,只能从顶部取放。
- 示例:若依次将元素
A, B, C
压入栈,则出栈顺序为C, B, A
。
2. 基本操作
- 压栈(Push):将元素添加到栈顶。
- 若栈已满(固定大小栈),可能触发栈溢出(Stack Overflow)错误。
- 弹栈(Pop):移除并返回栈顶元素。
- 若栈为空,可能触发栈下溢(Stack Underflow)错误。
- 取栈顶(Peek/Top):返回栈顶元素但不移除。
- 判空(IsEmpty):检查栈是否为空。
- 判满(IsFull,仅固定大小栈):检查栈是否已满。
3. 存储方式
- 数组实现:
- 固定大小,通过索引直接访问栈顶(如
top
指针指向当前栈顶)。 - 优点:实现简单,访问速度快。
- 缺点:大小固定,可能浪费空间或溢出。
- 固定大小,通过索引直接访问栈顶(如
- 链表实现:
- 动态大小,通过链表节点逐个连接。
- 优点:灵活,无溢出风险(除非内存耗尽)。
- 缺点:需要额外空间存储指针,操作稍慢。
4. 时间复杂度
- 压栈(Push):O(1)(数组或链表均常数时间)。
- 弹栈(Pop):O(1)。
- 取栈顶(Peek):O(1)。
- 判空/判满:O(1)。
5. 应用场景
- 函数调用栈:系统自动管理函数调用和返回地址。
- 表达式求值:如逆波兰表达式(后缀表达式)的计算。
- 括号匹配:检查括号是否成对出现。
- 深度优先搜索(DFS):辅助遍历或回溯。
- 撤销操作(Undo):记录操作历史,支持回退。
- 浏览器前进/后退:维护访问页面的历史记录。
6. 与队列的区别
- 队列(Queue):先进先出(FIFO),如排队场景。
- 栈:后进先出(LIFO),如递归调用或撤销操作。
7. 扩展特性
- 双栈共享空间:利用一个数组实现两个栈,提高空间利用率。
- 最小栈(Min Stack):支持在 O(1) 时间内获取栈内最小值。
- 单调栈:维护栈内元素的单调性,用于解决特定问题(如下一个更大元素)。
队列(Queue)是一种遵循先进先出(FIFO, First In First Out)原则的线性数据结构,其核心特性体现在操作规则、存储方式、应用场景及与栈的对比上。以下是队列的详细特性:
1. 先进先出(FIFO)原则
- 定义:最先入队的元素会最先出队,类似于现实中排队的场景(先到先服务)。
- 示例:若依次将元素
A, B, C
入队,则出队顺序为A, B, C
。
2. 基本操作
- 入队(Enqueue):将元素添加到队尾。
- 若队列已满(固定大小队列),可能触发队列溢出(Queue Overflow)错误。
- 出队(Dequeue):移除并返回队首元素。
- 若队列为空,可能触发队列下溢(Queue Underflow)错误。
- 取队首(Front/Peek):返回队首元素但不移除。
- 判空(IsEmpty):检查队列是否为空。
- 判满(IsFull,仅固定大小队列):检查队列是否已满。
3. 存储方式
- 数组实现(静态队列):
- 固定大小,通过两个指针(
front
和rear
)分别指向队首和队尾。 - 循环队列优化:解决“假溢出”问题(即队尾到达数组末尾但队首有空位时,通过取模运算循环利用空间)。
- 优点:实现简单,访问速度快。
- 缺点:大小固定,可能浪费空间或溢出。
- 固定大小,通过两个指针(
- 链表实现(动态队列):
- 动态大小,通过链表节点逐个连接。
- 优点:灵活,无溢出风险(除非内存耗尽)。
- 缺点:需要额外空间存储指针,操作稍慢。
4. 时间复杂度
- 入队(Enqueue):O(1)(数组或链表均常数时间)。
- 出队(Dequeue):O(1)。
- 取队首(Front/Peek):O(1)。
- 判空/判满:O(1)。
5. 应用场景
- 任务调度:操作系统中的进程调度、打印任务队列。
- 消息传递:消息队列(如 RabbitMQ、Kafka)实现异步通信。
- 广度优先搜索(BFS):辅助遍历图或树结构。
- 缓冲区管理:如网络数据包的缓存、键盘输入缓冲。
- 生产者-消费者模型:多线程中协调生产者和消费者的速度差异。
- 滑动窗口问题:维护窗口内的元素顺序(如最大值/最小值计算)。
6. 与栈的区别
特性 | 队列(Queue) | 栈(Stack) |
---|---|---|
原则 | 先进先出(FIFO) | 后进先出(LIFO) |
操作顺序 | 入队(队尾),出队(队首) | 压栈(栈顶),弹栈(栈顶) |
典型应用 | BFS、任务调度、消息队列 | DFS、函数调用、撤销操作 |
7. 扩展特性
- 双端队列(Deque):
- 支持在队首和队尾同时进行插入和删除操作(如 Python 的
collections.deque
)。
- 支持在队首和队尾同时进行插入和删除操作(如 Python 的
- 优先队列(Priority Queue):
- 元素按优先级出队(如最小堆或最大堆实现)。
- 阻塞队列:
- 当队列为空时,出队操作会阻塞直到有元素;队列满时,入队操作会阻塞直到有空位(常用于生产者-消费者模型)。
- 并发队列:
- 线程安全的队列实现(如 Java 的
ConcurrentLinkedQueue
),支持多线程并发操作。
- 线程安全的队列实现(如 Java 的