24、数据结构核心:队列与栈的原理、实现与应用

数据结构核心:队列与栈的原理、实现与应用

在计算机编程中,队列和栈是两种基础且高频使用的线性表数据结构,核心用于解决「数据缓冲」「顺序控制」「速度不匹配」等问题。本文将从原理、定义、实现三个维度,详细拆解队列(顺序/循环/链式)和栈的核心知识点,附完整结构体与函数接口设计。

一、队列:先进先出(FIFO)的线性表

1.1 队列核心定义

  • 本质:只允许在一端(队尾)插入数据,另一端(队头)删除数据的线性表
  • 核心特性:先进先出(First In First Out, FIFO)
  • 关键术语
    • 队尾(Tail):允许插入数据的一端
    • 队头(Head):允许删除数据的一端
  • 核心作用:缓冲数据、解决生产端与消费端速度不匹配问题(如IO缓冲、消息队列)
  • 常用操作:入队(Enter)、出队(Quit)、判空(IsEmpty)、判满(IsFull)、获取队头(GetHead)、销毁(Destroy)

1.2 队列的三种实现方式

1.2.1 顺序队列(基础版)

定义 :基于数组实现的队列,通过head(队头下标)和tail(队尾下标)控制数据插入与删除。

  • 核心缺陷headtail持续递增会导致数组越界,即使数组有空闲空间也无法使用(「假溢出」)
  • 结构体定义
c 复制代码
typedef int DATATYPE;
typedef struct queue {
    DATATYPE *ptr;   // 存储数据的数组指针
    int tlen;        // 数组总长度(队列最大容量)
    int head;        // 队头下标(指向队头元素)
    int tail;        // 队尾下标(指向队尾元素的下一个位置)
} SeqQueue;
  • 核心函数接口
c 复制代码
SeqQueue *CreateSeqQueue(int len);    // 创建顺序队列(指定容量)
int EnterSeqQueue(SeqQueue *queue, DATATYPE data);  // 入队
DATATYPE QuitSeqQueue(SeqQueue *queue);             // 出队
int IsEmptySeqQueue(SeqQueue *queue);               // 判空
int IsFullSeqQueue(SeqQueue *queue);                // 判满
int DestroySeqQueue(SeqQueue *queue);               // 销毁队列
1.2.2 循环队列(解决假溢出)

定义 :对顺序队列的优化,通过「取模运算」让headtail下标循环复用数组空间,解决假溢出问题。

  • 核心设计
    • 空队判断:head == tail(队头与队尾下标重合)
    • 满队判断:(tail + 1) % tlen == head(牺牲1个数组空间,避免空队与满队歧义)
    • 下标更新:head = (head + 1) % tlentail = (tail + 1) % tlen(循环递增)
  • 结构体定义 :与顺序队列一致(复用SeqQueue
  • 核心优势:空间利用率高,无假溢出问题,适合固定容量场景
1.2.3 链式队列(动态容量)

定义 :基于链表实现的队列,通过head(队头指针)和tail(队尾指针)控制节点插入与删除,无需预设容量。

  • 核心特点:动态扩容,无满队限制(除非内存耗尽),插入/删除操作效率O(1)
  • 结构体定义
c 复制代码
// 队列存储的数据类型(人员信息示例)
typedef struct person {
    char name[32];
    char sex;
    int age;
    int score;
} DATATYPE;

// 链式队列节点
typedef struct quenode {
    DATATYPE data;          // 节点存储的数据
    struct quenode *next;   // 指向下一节点的指针
} LinkQueNode;

// 链式队列管理结构体
typedef struct {
    LinkQueNode *head;      // 队头指针(指向第一个节点)
    LinkQueNode *tail;      // 队尾指针(指向最后一个节点)
    int clen;               // 当前队列长度(节点数)
} LinkQue;
  • 核心函数接口
c 复制代码
LinkQue *CreateLinkQue();                          // 创建空链式队列
int EnterLinkQue(LinkQue *lq, DATATYPE* newdata);  // 入队(尾插节点)
int QuitLinkQue(LinkQue* lq);                      // 出队(头删节点)
DATATYPE* GetHeadLinkQue(LinkQue *lq);             // 获取队头数据(不删除)
int GetSizeLinkQue(LinkQue *lq);                   // 获取队列长度
int IsEmptyLinkQue(LinkQue *lq);                   // 判空
int DestroyLinkQue(LinkQue *lq);                   // 销毁队列(释放所有节点)

1.3 三种队列对比

实现方式 核心优势 核心劣势 适用场景
顺序队列 实现简单 假溢出问题 入门学习,固定容量且数据量小
循环队列 空间利用率高,效率O(1) 容量固定 嵌入式开发、缓冲区设计(如串口缓冲)
链式队列 动态扩容,无满队限制 节点开销略大 数据量不确定、需要动态调整容量的场景

二、栈:先进后出(LIFO)的线性表

栈与队列同属线性表,核心特性为「先进后出(LIFO)」,常与队列配合使用,此处补充核心概念与类型划分。

2.1 栈的核心定义

  • 本质:只允许在一端(栈顶Top)进行插入和删除操作的线性表
  • 核心特性:先进后出(Last In First Out, LIFO)
  • 关键术语
    • 栈顶(Top):允许插入/删除的一端
    • 栈底(Bottom):固定不变的一端
  • 常用操作:入栈(Push)、出栈(Pop)、获取栈顶(GetTop)、判空(IsEmpty)、销毁(Destroy)

2.2 栈的核心类型划分

根据「栈顶指针(Top)变化方向」和「空/满栈定义」,栈可分为4类:

类型 核心定义
空增栈 增栈(Top地址递增)+ 空栈(Top指向新元素待插入位置)
空减栈 减栈(Top地址递减)+ 空栈(Top指向新元素待插入位置)
满增栈 增栈(Top地址递增)+ 满栈(Top指向最后入栈元素的位置)
满减栈 减栈(Top地址递减)+ 满栈(Top指向最后入栈元素的位置)
关键补充:
  • 增栈:新增元素后,Top指针指向的内存地址「逐渐变大」(向高地址扩展)
  • 减栈:新增元素后,Top指针指向的内存地址「逐渐变小」(向低地址扩展)
  • 空栈标识:Top指针指向「新元素待插入的位置」(此时栈内无数据)
  • 满栈标识:Top指针指向「最后入栈元素的位置」(此时栈内数据已满)

2.3 栈的函数接口设计

c 复制代码
typedef int DATATYPE;  // 栈存储的数据类型(可自定义)
typedef struct {
    DATATYPE *ptr;     // 栈空间指针
    int top;           // 栈顶指针(下标/地址,取决于实现)
    int size;          // 栈最大容量
} LinkStack;

// 栈核心函数接口
LinkStack* CreateLinkStack();                     // 创建栈(指定容量)
int PushLinkStack(LinkStack* ls, DATATYPE* newdata);  // 入栈
int PopLinkStack(LinkStack* ls);                  // 出栈
DATATYPE* GetTopLinkStack(LinkStack* ls);         // 获取栈顶元素
int GetSizeLinkStack(LinkStack* ls);              // 获取栈内元素个数
int IsEmptyLinkStack(LinkStack* ls);              // 判空
int DestroyLinkStack(LinkStack* ls);              // 销毁栈

三、核心总结

3.1 队列与栈的核心区别

数据结构 核心特性 典型应用场景
队列 先进先出(FIFO) 消息队列、IO缓冲、任务调度
先进后出(LIFO) 函数调用栈、表达式求值、括号匹配

3.2 实践选型建议

  1. 若需「固定容量+高效缓冲」:选择循环队列(嵌入式开发首选)
  2. 若需「动态容量+数据量不确定」:选择链式队列
  3. 若需「顺序执行+回溯逻辑」:选择(增栈更符合内存扩展习惯)
  4. 入门学习:先掌握顺序队列→循环队列→链式队列→栈,循序渐进理解线性表设计思想

3.3 关键注意事项

  • 队列/栈的「判空/判满」是核心,直接影响数据操作的正确性(如循环队列的空间牺牲设计)
  • 链式结构需注意「野指针」和「内存泄露」,销毁时必须遍历释放所有节点
  • 嵌入式场景中,循环队列因「无动态内存分配」更稳定,适合资源受限环境

际应用案例(如串口缓冲用循环队列实现)

  1. 代码调试技巧(如队列空满判断的常见bug)

可以随时告诉我,我会进一步扩展内容!

相关推荐
学编程的闹钟37 分钟前
75【虚拟主机和ftp】
学习
CoderYanger38 分钟前
A.每日一题——3625. 统计梯形的数目 II
java·算法·leetcode·职场和发展
豐儀麟阁贵44 分钟前
9.1String类
java·开发语言·算法
可可苏饼干1 小时前
Docker命令与知识点归纳
运维·学习·docker·容器
灰灰勇闯IT1 小时前
Flutter 适配 OpenHarmony 全流程实战:基于 GitCode 社区项目快速落地
笔记·学习·harmonyos
三炭先生1 小时前
计算机视觉算法--第一章:概述
人工智能·算法·计算机视觉
佳航张1 小时前
C语言经典100题---例001---组无重复数字的数
c语言·开发语言
chilavert3181 小时前
技术演进中的开发沉思-225 Prototype.js 框架
开发语言·javascript·原型模式
大大菜鸟一枚1 小时前
ARM交叉编译环境配置与Qt依赖库部署指南
开发语言·arm开发·qt