跳到主要内容
数据结构:栈与队列详解及实现 | 极客日志
C 算法
数据结构:栈与队列详解及实现 综述由AI生成 栈(Stack)和队列(Queue)两种基础数据结构。阐述了栈的后进先出(LIFO)特性及数组/链表实现方式,包含动态栈的 C 语言源码;讲解了队列的先进先出(FIFO)原则及链表实现优势,提供完整队列代码示例。同时分析了函数调用、表达式求值、消息队列等应用场景,并对比了两者差异,介绍了双端队列与优先队列等高级变体,帮助读者掌握数据结构的选择与应用。
菩提 发布于 2026/3/30 更新于 2026/5/27 37 浏览栈与队列:数据结构中的双生花
在计算机科学的世界里,栈和队列如同双生花般存在——它们看似相似却各有千秋,共同构成了最基本也是最强大的数据结构工具集。
1. 栈:后进先出的有序世界
1.1 概念及结构剖析
栈(Stack)是一种特殊的线性表,其核心特性是只允许在固定一端(栈顶)进行插入和删除操作 。这种结构遵循**后进先出(LIFO)**原则:最后进入的元素最先被移除。
关键术语解析 :
压栈/入栈(Push) :在栈顶插入新元素
出栈(Pop) :从栈顶删除元素
栈顶(Top) :唯一允许操作的一端
栈底(Bottom) :不允许操作的一端
结构可视化 :
栈顶 → D → C → B → A ← 栈底 出栈顺序:D → C → B → A
1.2 实现方式深度解析
数组 vs 链表实现
在栈的实现中,数组和链表各有优劣:
特性 数组实现 链表实现 内存使用 连续内存空间 非连续内存空间 扩容成本 较高(需重新分配) 较低(动态分配) 访问速度 O(1) 随机访问 O(n) 顺序访问 缓存友好性 高 低 实现复杂度 简单 中等
为什么数组实现更优?
对于栈这种主要在尾部操作的数据结构,数组的尾部插入/删除操作时间复杂度为 O(1),且 CPU 缓存预取机制对连续内存访问更友好。虽然扩容时需要重新分配内存,但通过倍增策略 可以摊还这一成本。
1.3 动态栈实现详解(附程序源码)
1. 定义一个动态栈 typedef int STDataType;
typedef struct Stack {
STDataType* _a;
int _top;
int _capacity;
} ST;
2. 初始化 void STInit (ST* pst) {
assert(pst);
pst->a = 0 ;
pst->capacity = 0 ;
pst->top = 0 ;
}
在这有两种写法,第一种是 top 指向栈顶,第二种是 top 指向栈顶的下一个位置。我个人更推荐第二种写法,这种写法的 top 类似于链表中的 size。
3. 销毁 void STDestroy (ST* pst) {
assert(pst);
free (pst->a);
pst->a = NULL ;
pst->top = pst->capacity = 0 ;
}
4. 入栈 void STPush (ST* pst, STDataType x) {
assert(pst);
if (pst->top == pst->capacity) {
int newcapcacity = pst->capacity == 0 ? 4 : pst->capacity * 2 ;
STDataType* tmp = (STDataType*)realloc (pst->a, newcapcacity * sizeof (STDataType));
if (tmp == NULL ) {
perror("realloc fail" );
}
pst->a = tmp;
pst->capacity = newcapcacity;
}
pst->a[pst->top] = x;
pst->top++;
}
5. 出栈 void STPop (ST* pst) {
assert(pst);
pst->top--;
}
6. 取栈顶数据 STDataType STTop (ST* pst) {
assert(pst);
assert(pst->top > 0 );
return pst->a[pst->top - 1 ];
}
7. 判空 bool STEmpty (ST* pst) {
assert(pst);
return pst->top == 0 ;
}
8. 获取数据个数 int STSize (ST* pst) {
assert(pst);
return pst->top;
}
9. 访问栈 #define _CRT_SECURE_NO_WARNINGS
#include "Stack.h"
int main () {
ST s;
STInit(&s);
STPush(&s, 1 );
STPush(&s, 2 );
STPush(&s, 3 );
STPush(&s, 4 );
while (!STEmpty(&s)) {
printf ("%d " , STTop(&s));
STPop(&s);
}
STDestroy(&s);
return 0 ;
}
10. 程序源码 #pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
typedef int STDataType;
typedef struct Stack {
STDataType* a;
int top;
int capacity;
} ST;
void STInit (ST* pst) ;
void STDestroy (ST* pst) ;
void STPush (ST* pst, STDataType x) ;
void STPop (ST* pst) ;
STDataType STTop (ST* pst) ;
bool STEmpty (ST* pst) ;
int STSize (ST* pst) ;
#define _CRT_SECURE_NO_WARNINGS
#include "Stack.h"
void STInit (ST* pst) {
assert(pst);
pst->a = 0 ;
pst->capacity = 0 ;
pst->top = 0 ;
}
void STDestroy (ST* pst) {
assert(pst);
free (pst->a);
pst->a = NULL ;
pst->top = pst->capacity = 0 ;
}
void STPush (ST* pst, STDataType x) {
assert(pst);
if (pst->top == pst->capacity) {
int newcapcacity = pst->capacity == 0 ? 4 : pst->capacity * 2 ;
STDataType* tmp = (STDataType*)realloc (pst->a, newcapcacity * sizeof (STDataType));
if (tmp == NULL ) {
perror("realloc fail" );
}
pst->a = tmp;
pst->capacity = newcapcacity;
}
pst->a[pst->top] = x;
pst->top++;
}
void STPop (ST* pst) {
assert(pst);
pst->top--;
}
STDataType STTop (ST* pst) {
assert(pst);
assert(pst->top > 0 );
return pst->a[pst->top - 1 ];
}
bool STEmpty (ST* pst) {
assert(pst);
return pst->top == 0 ;
}
int STSize (ST* pst) {
assert(pst);
return pst->top;
}
#define _CRT_SECURE_NO_WARNINGS
#include "Stack.h"
int main () {
ST s;
STInit(&s);
STPush(&s, 1 );
STPush(&s, 2 );
STPush(&s, 3 );
STPush(&s, 4 );
while (!STEmpty(&s)) {
printf ("%d " , STTop(&s));
STPop(&s);
}
STDestroy(&s);
return 0 ;
}
1.4 栈的应用场景
1. 函数调用栈 程序执行时,每次函数调用都会在栈中创建一个栈帧(Stack Frame) ,存储:
void funcA () {
int a = 10 ;
funcB();
}
void funcB () {
int b = 20 ;
}
中缀转后缀 :操作数栈和运算符栈
括号匹配 :遇到左括号入栈,右括号出栈
计算后缀表达式 :操作数入栈,遇到运算符出栈计算
2. 表达式求值 栈用于处理各种表达式:示例:(1 + 2) * 3的后缀表达式 1 2 + 3 *
3. 浏览器历史记录 class BrowserHistory :
def __init__ (self ):
self .back_stack = []
self .forward_stack = []
def visit (self, url ):
self .back_stack.append(url)
self .forward_stack = []
def back (self ):
if len (self .back_stack) > 1 :
self .forward_stack.append(self .back_stack.pop())
return self .back_stack[-1 ]
4. 撤销操作(Undo) public class TextEditor {
private StringBuilder text = new StringBuilder ();
private Stack<String> history = new Stack <>();
public void type (String words) {
history.push(text.toString());
text.append(words);
}
public void undo () {
if (!history.isEmpty()) {
text = new StringBuilder (history.pop());
}
}
}
2. 队列:先进先出的公平之师
2.1 概念及结构剖析 队列(Queue)是一种只允许在一端(队尾)插入 ,在另一端(队头)删除 的线性表。这种结构遵循**先进先出(FIFO)**原则:最先进入的元素最先被移除。
入队(Enqueue) :在队尾插入新元素
出队(Dequeue) :从队头删除元素
队头(Front) :删除操作端
队尾(Rear) :插入操作端
队头 → A → B → C → D → E ← 队尾 出队顺序:A → B → C → D → E
2.2 实现方式 实现方案 :队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。
为什么链表实现更优?
数组实现问题 :
出队操作需要移动所有元素(O(n) 时间复杂度)
假溢出问题(实际空间可用但无法入队)
需要复杂的环形缓冲区处理
链表实现优势 :
O(1) 时间复杂度的入队/出队操作
动态内存分配,无固定大小限制
自然避免假溢出问题
2.3 队列实现详解 (附程序源码)
核心数据结构定义 typedef int QDataType;
typedef struct QueueNode {
struct QueueNode * next ;
QDataType val;
} QNode;
typedef struct Queue {
QNode* phead;
QNode* ptail;
int size;
} Queue;
1. 初始化 void QueueInit (Queue* pq) {
assert(pq);
pq->phead = NULL ;
pq->ptail = NULL ;
pq->size = 0 ;
}
2. 队列的销毁 void QueueDestroy (Queue* pq) {
assert(pq);
QNode* cur = pq->phead;
while (cur) {
QNode* next = cur->next;
free (cur);
cur = next;
}
pq->phead = pq->ptail = NULL ;
pq->size = 0 ;
}
3. 队尾插入 void QueuePush (Queue* pq, QDataType x) {
QNode* newnode = (QNode*)malloc (sizeof (QNode));
if (newnode == NULL ) {
perror("malloc fail" );
return ;
}
newnode->next = NULL ;
newnode->val = x;
if (pq->ptail == NULL ) {
pq->phead = pq->ptail = newnode;
} else {
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
4. 队头删除 void QueuePop (Queue* pq) {
assert(pq);
assert(pq->phead);
if (pq->phead->next == NULL )
{
free (pq->phead);
pq->phead = pq->ptail = NULL ;
} else
{
QNode* next = pq->phead->next;
free (pq->phead);
pq->phead = next;
}
pq->size--;
}
5. 统计队列中数据个数 int QueueSize (Queue* pq) {
assert(pq);
return pq->size;
}
6. 取队头数据 QDataType QueueFront (Queue* pq) {
assert(pq);
assert(pq->phead);
return pq->phead->val;
}
7. 取队尾数据 QDataType QueueBack (Queue* pq) {
assert(pq);
assert(pq->phead);
return pq->ptail->val;
}
8. 判空 bool QueueEmpty (Queue* pq) {
assert(pq);
return pq->size == 0 ;
}
9. 访问队列 int main () {
Queue q;
QueueInit(&q);
QueuePush(&q, 1 );
QueuePush(&q, 2 );
QueuePush(&q, 3 );
QueuePush(&q, 4 );
while (!QueueEmpty(&q)) {
printf ("%d " , QueueFront(&q));
QueuePop(&q);
}
printf ("\n" );
return 0 ;
}
10. 程序源码 #pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
typedef int QDataType;
typedef struct QueueNode {
struct QueueNode * next ;
QDataType val;
} QNode;
typedef struct Queue {
QNode* phead;
QNode* ptail;
int size;
} Queue;
void QueueInit (Queue* pq) ;
void QueueDestroy (Queue* pq) ;
void QueuePush (Queue* pq, QDataType x) ;
void QueuePop (Queue* pq) ;
int QueueSize (Queue* pq) ;
QDataType QueueFront (Queue* pq) ;
QDataType QueueBack (Queue* pq) ;
bool QueueEmpty (Queue* pq) ;
#define _CRT_SECURE_NO_WARNINGS
#include "Queue.h"
void QueueInit (Queue* pq) {
assert(pq);
pq->phead = NULL ;
pq->ptail = NULL ;
pq->size = 0 ;
}
void QueuePush (Queue* pq, QDataType x) {
QNode* newnode = (QNode*)malloc (sizeof (QNode));
if (newnode == NULL ) {
perror("malloc fail" );
return ;
}
newnode->next = NULL ;
newnode->val = x;
if (pq->ptail == NULL ) {
pq->phead = pq->ptail = newnode;
} else {
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
int QueueSize (Queue* pq) {
assert(pq);
return pq->size;
}
void QueuePop (Queue* pq) {
assert(pq);
assert(pq->phead);
if (pq->phead->next == NULL )
{
free (pq->phead);
pq->phead = pq->ptail = NULL ;
} else
{
QNode* next = pq->phead->next;
free (pq->phead);
pq->phead = next;
}
pq->size--;
}
QDataType QueueFront (Queue* pq) {
assert(pq);
assert(pq->phead);
return pq->phead->val;
}
QDataType QueueBack (Queue* pq) {
assert(pq);
assert(pq->phead);
return pq->ptail->val;
}
bool QueueEmpty (Queue* pq) {
assert(pq);
return pq->size == 0 ;
}
void QueueDestroy (Queue* pq) {
assert(pq);
QNode* cur = pq->phead;
while (cur) {
QNode* next = cur->next;
free (cur);
cur = next;
}
pq->phead = pq->ptail = NULL ;
pq->size = 0 ;
}
#define _CRT_SECURE_NO_WARNINGS
#include "Queue.h"
int main () {
Queue q;
QueueInit(&q);
QueuePush(&q, 1 );
QueuePush(&q, 2 );
QueuePush(&q, 3 );
QueuePush(&q, 4 );
while (!QueueEmpty(&q)) {
printf ("%d " , QueueFront(&q));
QueuePop(&q);
}
printf ("\n" );
return 0 ;
}
2.4 队列的应用场景
1. 消息队列系统 public class MessageQueue {
private Queue<Message> queue = new LinkedList <>();
public synchronized void enqueue (Message msg) {
queue.add(msg);
notifyAll();
}
public synchronized Message dequeue () throws InterruptedException {
while (queue.isEmpty()) {
wait();
}
return queue.remove();
}
}
2. 打印机任务调度 class Printer :
def __init__ (self ):
self .print_queue = deque()
self .current_task = None
def add_document (self, document ):
self .print_queue.append(document)
print (f"Added document: {document} " )
def print_next (self ):
if self .print_queue:
self .current_task = self .print_queue.popleft()
print (f"Printing: {self.current_task} " )
else :
print ("No documents to print" )
3. 广度优先搜索(BFS) void BFS (Graph* graph, int start) {
bool visited[MAX_VERTICES] = {false };
Queue queue ;
QueueInit(&queue );
visited[start] = true ;
QueuePush(&queue , start);
while (!QueueEmpty(&queue )) {
int current = QueueFront(&queue );
QueuePop(&queue );
printf ("Visited %d\n" , current);
for (int i = 0 ; i < graph->vertices; i++) {
if (graph->adjMatrix[current][i] && !visited[i]) {
visited[i] = true ;
QueuePush(&queue , i);
}
}
}
}
4. CPU 任务调度 struct Task {
int pid;
int priority;
};
void scheduleTasks (Queue* highPriority, Queue* normalQueue) {
while (!QueueEmpty(highPriority) || !QueueEmpty(normalQueue)) {
Task* task;
if (!QueueEmpty(highPriority)) {
task = QueueFront(highPriority);
QueuePop(highPriority);
} else {
task = QueueFront(normalQueue);
QueuePop(normalQueue);
}
executeTask(task);
if (!task->completed) {
if (task->priority == HIGH) {
QueuePush(highPriority, task);
} else {
QueuePush(normalQueue, task);
}
}
}
}
3. 栈与队列的对比分析 特性 栈 (Stack) 队列 (Queue) 操作原则 LIFO (后进先出) FIFO (先进先出) 插入位置 栈顶 (Top) 队尾 (Rear) 删除位置 栈顶 (Top) 队头 (Front) 典型操作 Push, Pop Enqueue, Dequeue 实现方式 数组 (更优)/链表 链表 (更优)/循环数组 空间复杂度 O(n) O(n) 时间复杂度 Push/Pop: O(1) Enqueue/Dequeue: O(1) 应用场景 函数调用、表达式求值、回溯 消息传递、BFS、缓冲、调度 抽象层次 递归结构 管道结构
4. 高级变体与应用
4.1 双端队列(Deque) 双端队列 结合了栈和队列的特性,允许在两端进行插入和删除操作:
typedef struct DequeNode {
int data;
struct DequeNode * prev ;
struct DequeNode * next ;
} DequeNode;
typedef struct {
DequeNode* front;
DequeNode* rear;
} Deque;
void insertFront (Deque* dq, int data) {
DequeNode* newNode = createNode(data);
if (dq->front == NULL ) {
dq->front = dq->rear = newNode;
} else {
newNode->next = dq->front;
dq->front->prev = newNode;
dq->front = newNode;
}
}
int deleteRear (Deque* dq) {
if (dq->rear == NULL ) return -1 ;
DequeNode* temp = dq->rear;
int data = temp->data;
if (dq->front == dq->rear) {
dq->front = dq->rear = NULL ;
} else {
dq->rear = dq->rear->prev;
dq->rear->next = NULL ;
}
free (temp);
return data;
}
4.2 优先队列(Priority Queue) typedef struct {
int * heap;
int capacity;
int size;
} PriorityQueue;
void enqueue (PriorityQueue* pq, int value) {
if (pq->size == pq->capacity) {
}
int i = pq->size++;
pq->heap[i] = value;
while (i != 0 && pq->heap[(i - 1 ) / 2 ] > pq->heap[i]) {
swap(&pq->heap[i], &pq->heap[(i - 1 ) / 2 ]);
i = (i - 1 ) / 2 ;
}
}
int dequeue (PriorityQueue* pq) {
if (pq->size <= 0 ) return INT_MIN;
int root = pq->heap[0 ];
pq->heap[0 ] = pq->heap[--pq->size];
int i = 0 ;
while (true ) {
int smallest = i;
int left = 2 * i + 1 ;
int right = 2 * i + 2 ;
if (left < pq->size && pq->heap[left] < pq->heap[smallest]) smallest = left;
if (right < pq->size && pq->heap[right] < pq->heap[smallest]) smallest = right;
if (smallest != i) {
swap(&pq->heap[i], &pq->heap[smallest]);
i = smallest;
} else {
break ;
}
}
return root;
}
5. 总结:选择合适的数据结构 栈和队列作为基础数据结构,在算法设计和系统开发中无处不在:
选择栈的场景 :
需要回溯操作(如撤销功能)
递归算法实现
深度优先搜索(DFS)
语法解析和表达式求值
选择队列的场景 :
需要公平处理(如任务调度)
广度优先搜索(BFS)
缓冲区和数据传输
消息传递系统
混合使用场景 :
使用队列实现栈(需要两个队列)
使用栈实现队列(需要两个栈)
双端队列满足双向操作需求
优先队列处理带优先级的任务
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
Gemini 图片去水印 基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online