当前位置: 首页 > news >正文

栈和队列(C语言)

文章目录

    • 前言
    • 一、栈的概念、应用与结构
      • 1.1 栈的定义与特性
      • 1.2 栈的应用场景
      • 1.3 栈的逻辑结构示意
    • 二、栈的多种实现
      • 2.1 顺序栈(基于数组)
        • 2.1.1 数据结构
        • 2.1.2 主要操作
        • 2.1.3 优缺点
      • 2.2 链式栈(基于单链表)
        • 2.2.1 数据结构
        • 2.2.2 主要操作
        • 2.2.3 优缺点
    • 三、队列的概念、应用与结构
      • 3.1 队列的定义与特性
      • 3.2 队列的应用场景
      • 3.3 队列的逻辑结构示意
    • 四、队列的多种实现
      • 4.1 顺序队列(基于数组)
        • 4.1.1 数据结构
        • 4.1.2 主要操作
        • 4.1.3 问题
      • 4.2 循环队列
        • 4.2.1 数据结构
        • 4.2.2 主要操作
        • 4.2.3 优点
      • 4.3 链式队列
        • 4.3.1 数据结构
        • 4.3.2 主要操作
    • 五、复杂度与选型建议
    • 总结

前言

栈(Stack)和队列(Queue)是最基础、最常用的两种线性数据结构。

  • :遵循“后进先出(LIFO)”原则,常用于函数调用管理、表达式求值、括号匹配等场景;
  • 队列:遵循“先进先出(FIFO)”原则,常用于任务调度、缓存管理、广度优先搜索等场景。

本篇博客将从概念、典型应用、实现细节,到性能分析,分步骤带你深入掌握各类实现方法。

在这里插入图片描述


一、栈的概念、应用与结构

1.1 栈的定义与特性

  • 定义:栈是一种只能在一端进行插入和删除操作的线性结构。
  • 操作:入栈(Push)、出栈(Pop)、取栈顶(Top/Peek)、判空(IsEmpty)。
  • 特性
    • 后进先出(LIFO)
    • 操作时间复杂度均为 O(1)

1.2 栈的应用场景

  1. 函数调用:程序运行时,操作系统用调用栈保存返回地址与局部变量。
  2. 表达式求值:中缀表达式转后缀、后缀表达式计算都依赖双栈算法。
  3. 括号匹配:编译器检查源代码括号是否成对匹配。
  4. 图的深度优先搜索(DFS):可用显式栈代替递归调用。

1.3 栈的逻辑结构示意

栈

┌───────┐    ← 栈顶 (top)
│   30  │
│   20  │
│   10  │
└───────┘    ← 栈底

二、栈的多种实现

2.1 顺序栈(基于数组)

2.1.1 数据结构
#define MAXSIZE 100typedef struct {int data[MAXSIZE];int top;        // 指向当前栈顶元素的下标,-1 表示空栈
} SeqStack;
2.1.2 主要操作
// 初始化
void InitSeqStack(SeqStack *s) {s->top = -1;
}// 入栈
int PushSeq(SeqStack *s, int v) {if (s->top >= MAXSIZE - 1) return 0;  // 栈满s->data[++s->top] = v;return 1;
}// 出栈
int PopSeq(SeqStack *s, int *v) {if (s->top == -1) return 0;          // 栈空*v = s->data[s->top--];return 1;
}// 读栈顶
int TopSeq(SeqStack *s, int *v) {if (s->top == -1) return 0;*v = s->data[s->top];return 1;
}// 判空
int IsEmptySeq(SeqStack *s) {return s->top == -1;
}
2.1.3 优缺点
  • 优点:实现简单、访问速度快。
  • 缺点:容量固定,易出现“栈满”问题;插入和删除始终在尾部。

2.2 链式栈(基于单链表)

2.2.1 数据结构
typedef struct StackNode {int data;struct StackNode *next;
} StackNode, *LinkStack;
2.2.2 主要操作
// 初始化
void InitLinkStack(LinkStack *s) {*s = NULL;
}// 入栈:在链表头插入
void PushLink(LinkStack *s, int v) {StackNode *node = (StackNode*)malloc(sizeof(StackNode));node->data = v;node->next = *s;*s = node;
}// 出栈:删除链表头
int PopLink(LinkStack *s, int *v) {if (*s == NULL) return 0;StackNode *tmp = *s;*v = tmp->data;*s = tmp->next;free(tmp);return 1;
}// 取栈顶
int TopLink(LinkStack s, int *v) {if (s == NULL) return 0;*v = s->data;return 1;
}// 判空
int IsEmptyLink(LinkStack s) {return s == NULL;
}
2.2.3 优缺点
  • 优点:动态分配,空间利用率高,可存储更多数据;
  • 缺点:每次入/出栈都要 malloc/free,额外开销大。

三、队列的概念、应用与结构

3.1 队列的定义与特性

  • 定义:队列是一种只能在一端插入(队尾)、另一端删除(队头)的线性结构。
  • 操作:入队(Enqueue)、出队(Dequeue)、取队头(Front)、判空(IsEmpty)。
  • 特性
    • 先进先出(FIFO)
    • 基本操作时间复杂度均为 O(1)

3.2 队列的应用场景

  1. 操作系统进程调度:就绪队列按照先来先服务。
  2. 缓存/缓冲区:网络数据包、IO 缓冲区常用循环队列。
  3. 广度优先搜索(BFS):图和树遍历。
  4. 异步消息处理:生产者—消费者模型。

3.3 队列的逻辑结构示意

在这里插入图片描述

front → [10] [20] [30] ← rear

四、队列的多种实现

4.1 顺序队列(基于数组)

4.1.1 数据结构
#define MAXQ 100typedef struct {int data[MAXQ];int front;      // 指向队头元素下标int rear;       // 指向队尾下一个位置
} SeqQueue;
4.1.2 主要操作
// 初始化
void InitSeqQueue(SeqQueue *q) {q->front = q->rear = 0;
}// 入队
int EnQueue(SeqQueue *q, int v) {if (q->rear == MAXQ) return 0; // 队满q->data[q->rear++] = v;return 1;
}// 出队
int DeQueue(SeqQueue *q, int *v) {if (q->front == q->rear) return 0; // 队空*v = q->data[q->front++];return 1;
}// 取队头
int Front(SeqQueue *q, int *v) {if (q->front == q->rear) return 0;*v = q->data[q->front];return 1;
}// 判空
int IsEmptyQ(SeqQueue *q) {return q->front == q->rear;
}
4.1.3 问题
  • 随着 DeQueue 操作,front 索引不断增大,数组前部空间无法复用。

4.2 循环队列

4.2.1 数据结构
#define MAXQ 100typedef struct {int data[MAXQ];int front;      // 指向队头int rear;       // 指向队尾的下一个位置
} CircQueue;
4.2.2 主要操作
// 初始化
void InitCircQueue(CircQueue *q) {q->front = q->rear = 0;
}// 判满:下一位置等于 front 则满
int IsFullCirc(CircQueue *q) {return (q->rear + 1) % MAXQ == q->front;
}// 入队
int EnCirc(CircQueue *q, int v) {if (IsFullCirc(q)) return 0;q->data[q->rear] = v;q->rear = (q->rear + 1) % MAXQ;return 1;
}// 出队
int DeCirc(CircQueue *q, int *v) {if (IsEmptyQ((SeqQueue*)q)) return 0; // front==rear*v = q->data[q->front];q->front = (q->front + 1) % MAXQ;return 1;
}
4.2.3 优点
  • 空间利用率高,不会“假溢出”;
  • 逻辑与顺序队列相同,但索引循环使用。

4.3 链式队列

4.3.1 数据结构
typedef struct QNode {int data;struct QNode *next;
} QNode, *QueuePtr;typedef struct {QueuePtr front, rear;
} LinkQueue;
4.3.2 主要操作
// 初始化
void InitLinkQueue(LinkQueue *q) {q->front = q->rear = (QueuePtr)malloc(sizeof(QNode));q->front->next = NULL;
}// 入队:在链尾插入
void EnLink(LinkQueue *q, int v) {QueuePtr node = (QueuePtr)malloc(sizeof(QNode));node->data = v;node->next = NULL;q->rear->next = node;q->rear = node;
}// 出队:删除链头第一个节点
int DeLink(LinkQueue *q, int *v) {if (q->front == q->rear) return 0; // 空队QueuePtr tmp = q->front->next;*v = tmp->data;q->front->next = tmp->next;if (q->rear == tmp) q->rear = q->front;free(tmp);return 1;
}

五、复杂度与选型建议

实现方式空间复杂度入/出操作优点适用场景
顺序栈O(n)O(1)简单、访问快元素个数上限已知、对性能要求高
链式栈O(n)O(1)动态、无栈满限制元素个数动态、空间不预先分配
顺序队列O(n)O(1)简单入/出次数少、不关注循环复用
循环队列O(n)O(1)空间高效、不需搬移元素缓冲区、IO 队列、实时任务调度
链式队列O(n)O(1)动态、无队满限制元素动态、空闲链表场景

总结

  1. 理解本质:栈“后进先出”,队列“先进先出”;
  2. 灵活选型:根据元素规模、空间/性能需求,选用顺序或链式实现;
  3. 掌握变体:循环队列可解决数组队列的“假溢出”问题;

相关文章:

  • Windows 10 下安装 PHP 问题解决指南
  • 算法笔记—动态规划
  • 第36讲:作物生长预测中的时间序列建模(LSTM等)
  • 基于Python的推荐算法的电影推荐系统的设计
  • Linux 学习 6 文件相关命令包含查询
  • 运筹学之粒子群算法
  • Day3:个人中心页面布局前端项目uniapp壁纸实战
  • Windows 10 上安装 Spring Boot CLI详细步骤
  • 深入理解 Java 中的 synchronized 关键字
  • Python异常(九)
  • Linux:进程间通信
  • flowable-流程和表使用教程
  • 如何优雅地实现全局唯一?深入理解单例模式
  • MAC-exists,表字段要不要建索引
  • Linux 进程控制(自用)
  • Kubernetes相关的名词解释Metrics Server组件(7)
  • 东京 ⇄ 京都游记⛩️
  • 漫游git rebase + 浅谈git checkout和git branch -f的分支命令
  • 5 提示词工程指南-计划与行动
  • 4.19-4.20学习总结 网络编程+反射+动态代理
  • 4月译著联合书单|心爱之物:热爱如何联结并塑造我们
  • 体坛联播|欧冠半决赛阿森纳主场不敌巴黎,北京男篮险胜山西
  • 向总书记汇报具身智能发展的“稚辉君”:从期待到兴奋再到备受鼓舞
  • 烟花、美食和购物优惠都安排上了,上海多区开启热闹模式
  • 王毅:坚持金砖团结合作,改革完善全球治理
  • 解放日报头版头条:“五个中心”蹄疾步稳谱新篇