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

栈和队列的实现

一、数据结构概述

栈(Stack) 和 队列(Queue) 是两种基础且重要的线性数据结构:

  • :后进先出(LIFO),支持入栈(Push)、出栈(Pop)、获取栈顶元素(Top)等操作。典型应用场景包括函数调用栈、表达式求值、括号匹配等。

  • 队列:先进先出(FIFO),支持入队(Push)、出队(Pop)、获取队首/队尾元素(Front/Back)等操作。典型应用场景包括任务调度、缓冲区管理等。

二、队列的单链表实现

1. 核心数据结构

// 队列节点(单链表)
typedef struct QListNode {int val;                 // 存储数据struct QListNode* next;  // 指向下一个节点
} QNode;// 队列结构
typedef struct Queue {QNode* front;  // 队首指针(出队位置)QNode* tail;   // 队尾指针(入队位置)int size;      // 队列当前元素数量
} Queue;

2. 关键操作实现

入队(QueuePush

  • 逻辑:在队尾添加新节点,维护tail指针。

  • 时间复杂度:O(1)

void QueuePush(Queue* q, int x) {QNode* NewNode = (QNode*)malloc(sizeof(QNode));NewNode->val = x;NewNode->next = NULL;if (q->front == NULL) {  // 队列为空q->front = q->tail = NewNode;} else {                 // 队列非空q->tail->next = NewNode;q->tail = NewNode;}q->size++;
}

出队(QueuePop

  • 逻辑:删除队首节点,维护front指针。

  • 时间复杂度:O(1)

void QueuePop(Queue* q) {if (q->front == q->tail) {  // 只有一个元素free(q->front);q->front = q->tail = NULL;} else {                   // 多个元素QNode* tmp = q->front;q->front = q->front->next;free(tmp);}q->size--;
}

 判空:检查front指针是否为NULL(时间复杂度O(1))

bool QueueEmpty(Queue* q) 
{assert(q); // 确保队列指针非空return q->front == NULL; // 队首为空表示队列为空
}

销毁队列:循环释放所有节点内存,避免内存泄漏。

//销毁队列
void QueueDestroy(Queue* q)
{assert(q);assert(q->front);while (q->size != 0){if (q->front == q->tail){free(q->front);}else{QNode* tmp = q->front->next;free(q->front);q->front = tmp;}q->size--;}q->front = NULL;q->tail = NULL;q->size = 0;
}

三、栈的动态数组实现 

 1. 核心数据结构

typedef int STDataType;typedef struct Stack {STDataType* arr;  // 动态数组int top;          // 栈顶指针(指向下一个可用位置)int capacity;     // 当前容量
} ST;

2. 关键操作实现

入栈(STPush

  • 逻辑:动态扩容后插入元素到栈顶。

  • 时间复杂度:均摊O(1)

void STPush(ST* ps, STDataType x) {CheckCapacity(ps);  // 检查并扩容ps->arr[ps->top] = x;ps->top++;
}// 动态扩容函数
void CheckCapacity(ST* ps) {if (ps->capacity == ps->top) {int newcapacity = (ps->capacity == 0) ? 4 : 2 * ps->capacity;STDataType* tmp = realloc(ps->arr, newcapacity * sizeof(STDataType));ps->arr = tmp;ps->capacity = newcapacity;}
}

出栈(STPop

  • 逻辑:仅移动栈顶指针,无需实际删除数据。

  • 时间复杂度:O(1)

void STPop(ST* ps) {assert(!STEmpty(ps));ps->top--;
}
  • 获取栈顶元素:返回arr[top - 1](时间复杂度O(1))。

STDataType STTop(ST* ps)//输出栈顶元素
{assert(ps);assert(!STEmpty(ps));return ps->arr[ps->top - 1];
}
  • 判空:检查top是否为0(时间复杂度O(1))。

bool STEmpty(ST* ps) 
{assert(ps); // 确保栈指针非空return ps->top == 0; // 栈顶指针为0表示空栈
}

 四、实现对比与性能分析

 1. 数据结构选择与内存管理

特性队列(单链表)栈(动态数组)
数据结构单链表(离散内存)动态数组(连续内存)
内存分配动态分配节点,按需增长预先分配连续内存,按需扩容
内存开销每个节点需额外存储指针(空间开销大)无额外指针,仅存储数据(空间紧凑)
扩容/缩容无需扩容,直接插入新节点需动态扩容(通常翻倍),可能内存复制

 2. 核心操作复杂度

操作队列(单链表)栈(动态数组)
插入(Push)O(1)均摊O(1)(扩容时可能O(n))
删除(Pop)O(1)O(1)
访问头部O(1)(直接通过front指针)不支持(栈仅允许访问栈顶)
随机访问不支持支持(但受栈操作限制)

3. 优点与缺点

1. 队列(单链表)

  • 优点

    • 动态大小:无需预先分配内存,适合元素数量不固定的场景。

    • 高效操作:入队(尾部插入)和出队(头部删除)均为O(1)。

    • 灵活性:适合频繁插入和删除的场景(如任务调度)。

  • 缺点

    • 内存碎片:节点离散存储,可能导致内存碎片。

    • 额外指针开销:每个节点需存储next指针,空间利用率较低。

    • 遍历成本高:若需遍历队列,时间复杂度为O(n)。

2 栈(动态数组)

  • 优点

    • 内存紧凑:连续存储,缓存友好,访问速度快。

    • 高效扩容:均摊时间复杂度为O(1)(扩容策略优化)。

    • 快速操作:入栈和出栈均为O(1),适合高频操作场景。

  • 缺点

    • 扩容开销:扩容时需复制数据,可能引发短暂性能下降。

    • 容量限制:初始容量需合理设置,否则频繁扩容影响效率。

    • 内存浪费:若容量远大于实际需求,可能造成内存浪费。

3. 关键设计差异

  • 操作逻辑

    • 队列是先进先出(FIFO),操作分别在队尾(入队)和队首(出队)。

    • 栈是后进先出(LIFO),所有操作集中在栈顶。

  • 内存管理

    • 队列通过指针维护动态链表,内存灵活但碎片化。

    • 栈通过动态数组管理内存,连续存储但需处理扩容。

  • 错误处理

    • 队列需处理空队列的Pop操作(代码中使用assert强制检查)。

    • 栈需处理空栈的PopTop操作,以及扩容失败时的内存分配问题。

4. 总结

队列和栈在实现上的核心差异源于其操作特性(FIFO vs LIFO)和底层数据结构的选择(链表 vs 数组)。

  • 队列的单链表实现牺牲空间效率换取了操作的灵活性和动态扩展能力。

  • 栈的动态数组实现以潜在扩容开销为代价,换取了内存紧凑性和高速访问性能。

选择队列(链表)的情况

  • 需要频繁插入和删除,且元素数量不可预测。

  • 对内存碎片不敏感,但要求严格O(1)操作时间。

选择栈(数组)的情况

  • 元素数量相对稳定或可预测。

  • 需要高频访问栈顶元素,且追求内存连续性带来的性能优势。

 

 

相关文章:

  • Redis 8.0 GA,重回开源
  • Vulkan 动态渲染
  • LeetCode222_完全二叉树的结点个数
  • 补充Depends 和 request: Request 依赖注入用法的注意事项
  • 计算机组成原理第2章(竟成)
  • upload-labs通关笔记-第15关 文件上传之getimagesize绕过(图片马)
  • DeepSeek源码解构:从MoE架构到MLA的工程化实现
  • Ajax研究
  • 山东大学高级程序设计期末复习
  • 【C++】从零认识C++的“继承”
  • Flink-Yarn运行模式
  • C++ 中,派生类什么时候可以不定义构造函数,什么时候必须定义构造函数?
  • Flink 核心概念解析:流数据、并行处理与状态
  • TDengine 运维—容量规划
  • leetcode 25. Reverse Nodes in k-Group
  • 鸿蒙HarmonyOS最新的组件间通信的装饰器与状态组件详解
  • SpringMVC 通过ajax 实现文件的上传
  • 关于光谱相机的灵敏度
  • naive-ui切换主题
  • 实验分享|基于千眼狼sCMOS科学相机的流式细胞仪细胞核成像实验
  • 免费综合网站注册申请/seo推广技术
  • 北京知名网站设计公司/网络营销个人感悟小结
  • 网站开发转包协议/漯河seo推广
  • 做网站卖什么/网络营销推广计划
  • 做会计应关注什么网站/小网站
  • 投资公司的经营范围有哪些/seo模拟点击算法