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

C语言双端队列完整实现:一行代码吃透头尾操作,算法效率拉满

一、为什么C语言实现双端队列,是数据结构的必学天花板?

在C语言数据结构里,队列、栈都是基础中的基础,但真正能把灵活度、效率、内存管理三者揉到一起的,还得是双端队列(deque)。

普通队列只能一头进一头出,栈只能先进后出,而双端队列可以两头插、两头删,既兼容队列FIFO,又支持栈LIFO,堪称数据结构里的“全能选手”。

很多人学数据结构,只停留在理论,一到C语言手写就卡壳:指针不会用、内存容易漏、扩容逻辑写不明白。而今天这套完整实现,把双向链表、动态扩容、泛型设计、安全释放全部打通,所有操作全是O(1)时间复杂度,工程级可用,看完就能直接写进项目里。

它不是玩具代码,而是能真正用于面试、作业、实际开发的稳健实现。

二、核心拆解:双端队列结构与完整代码实现1. 双端队列核心结构设计

双端队列的关键,是用双向链表节点串联,再用一个总结构管理头尾、大小、容量和元素大小,实现与类型无关的通用队列。

// 双向链表节点 typedef struct deque_node { void *data; // 数据指针 struct deque_node *next; // 后驱节点 struct deque_node *previous; // 前驱节点 } deque_node; // 双端队列总结构 typedef struct deque { struct deque_node *front; // 队头 struct deque_node *rear; // 队尾 size_t size; // 当前元素个数 size_t capacity; // 容量 size_t elem_size; // 单个元素大小 } deque;

2. 队列初始化

先做参数安全校验,再给默认容量,从根源避免野指针和非法访问。

int deque_init(deque *dq, size_t elem_size) { if (dq == NULL || elem_size == 0) { return -1; } dq->front = NULL; dq->rear = NULL; dq->size = 0; dq->capacity = 8; // 初始默认8个容量 dq->elem_size = elem_size; return 0; }

3. 头部插入 & 尾部插入

支持队头加元素、队尾加元素,满了自动扩容2倍,不用手动管理。

// 头部插入 int dq_push_front(deque *dq, const void *value) { if (dq == NULL || value == NULL) return -1; if (dq->size >= dq->capacity) dq->capacity *= 2; deque_node *node = malloc(sizeof(deque_node)); node->data = malloc(dq->elem_size); memcpy(node->data, value, dq->elem_size); if (dq->front == NULL) { node->next = NULL; node->previous = NULL; dq->front = node; dq->rear = node; } else { node->next = dq->front; node->previous = NULL; dq->front->previous = node; dq->front = node; } dq->size++; return 0; } // 尾部插入 int dq_push_back(deque *dq, const void *value) { if (dq == NULL || value == NULL) return -1; if (dq->size >= dq->capacity) dq->capacity *= 2; deque_node *node = malloc(sizeof(deque_node)); node->data = malloc(dq->elem_size); memcpy(node->data, value, dq->elem_size); if (dq->front == NULL) { node->next = NULL; node->previous = NULL; dq->front = node; dq->rear = node; } else { node->next = NULL; node->previous = dq->rear; dq->rear->next = node; dq->rear = node; } dq->size++; return 0; }

4. 头部删除 & 尾部删除

删完自动把数据拷贝出来,并安全释放内存,不产生内存泄漏。

// 头部删除 int dq_pop_front(deque *dq, void *value) { if (dq == NULL || dq->size == 0 || value == NULL) return -1; deque_node *node = dq->front; dq->front = dq->front->next; dq->front->previous = NULL; dq->size--; memcpy(value, node->data, dq->elem_size); free(node->data); free(node); return 0; } // 尾部删除 int dq_pop_back(deque *dq, void *value) { if (dq == NULL || dq->size == 0 || value == NULL) return -1; deque_node *node = dq->rear; dq->rear = dq->rear->previous; dq->rear->next = NULL; dq->size--; memcpy(value, node->data, dq->elem_size); free(node->data); free(node); return 0; }

5. 查看队头队尾 & 工具函数

只看不删,方便判断、遍历、调试。

// 查看队头 int dq_peek_front(deque *dq, void *value) { if (dq == NULL || dq->size == 0 || value == NULL) return -1; memcpy(value, dq->front->data, dq->elem_size); return 0; } // 查看队尾 int dq_peek_back(deque *dq, void *value) { if (dq == NULL || dq->size == 0 || value == NULL) return -1; memcpy(value, dq->rear->data, dq->elem_size); return 0; } // 判断空 int dq_is_empty(deque *dq) { if (dq == NULL) return -1; return dq->size == 0 ? 0 : 1; } // 获取大小 int dq_size(deque *dq) { if (dq == NULL) return -1; return dq->size; }

6. 效率总结

这套双端队列所有核心操作:

时间复杂度:O(1)

空间复杂度:O(1)

效率稳定,适合高频操作场景。

三、辩证思考:这样实现双端队列,真的完美吗?

这套实现非常经典、完整,但放在真实开发里,依然有值得权衡的地方。

从优点看:

但也有客观短板:

这不是代码的问题,而是数据结构本身的取舍:你要灵活,就要牺牲一点连续内存;你要O(1)头尾操作,就很难兼顾极致紧凑。

真正优秀的程序员,不是只会抄代码,而是知道什么时候用链表deque,什么时候用数组deque。

四、现实意义:学会这套,对C语言开发者意味着什么?

很多人觉得数据结构是面试题,写完就忘。但这套双端队列,几乎把C语言核心难点全练到了:

能独立写出这套代码,基本说明:

不管是考研机试、校招面试、课程设计,都是直接加分项。

在实际项目里,双端队列常用于:

学会它,等于多了一把高效处理数据的利器。

五、互动话题:你在写数据结构时,最容易踩哪些坑?

双端队列看起来简单,真正手写时,很多人都会栽在同一个地方:

你在实现队列、栈、链表时,遇到过最头疼的问题是什么?

你更习惯用链表实现deque,还是数组循环队列实现deque?

欢迎在评论区留下你的思路,一起把C语言数据结构彻底吃透。

http://www.jsqmd.com/news/887435/

相关文章:

  • 使用Taotoken CLI工具一键配置开发环境,支持多种AI助手工具
  • 别再傻傻分不清:Mol、SDF、SMILES文件格式到底怎么选?
  • 智能手机相机光谱特性测量与多光谱成像技术
  • 揭秘生物年龄计算:BioAge工具包如何帮你量化衰老进程
  • gr-filter 滤波与多速率模块完整源码分析
  • 在Ubuntu 18.04上搞定Anubis 2.3静态版:从下载、配置到跑通第一个GNSS数据质量分析
  • 高性能Windows流媒体服务器部署:5大核心技术与3种实战架构深度解析
  • modelscope v1.37.1 修复 trust_remote_code 兼容性问题:一次看懂 2026-05-22 最新补丁版全部更新
  • iPaaS 应用场景深度解析:从系统孤岛到数据自由流动的六大实战路径
  • Windows自带的硬盘医生:当移动硬盘提示0x80070570时,除了CHKDSK你还可以试试这些方法
  • i7-10850H 和 T2000 显卡 的 HP ZBook Fury 15 G7
  • 淘金币自动化脚本:5分钟完成所有淘宝任务的终极指南
  • 为什么92%的团队误判DeepSeek生成代码的安全性?——一份被封存的内部质量审计报告(限时公开)
  • 告别录屏软件!用Unity Recorder在编辑器内搞定游戏宣传片(附Timeline联动教程)
  • 拾亩绿光纯亚麻籽微粉哪里靠谱
  • 基于ATtiny85与JQ8900-16P的极简嵌入式音频播放系统设计与实现
  • (毕业必看)实测靠谱的AI论文软件,毕业党收藏备用
  • 低精度神经网络训练:LMD算法与MXFP6技术解析
  • 基于Arduino与ACS712的智能待机功耗控制方案设计与实现
  • 2026现阶段温州实木全屋定制优质公司联系全攻略 - 2026年企业推荐榜
  • Sora 2商用红线预警:版权溯源链构建指南(含AI生成视频DCI数字版权登记全流程)
  • 从零到一:在LUNIX系统上部署Anubis并进行GNSS数据质量分析
  • 2026-05-26:移除前缀使数组严格递增。用go语言,给定整数数组 nums,你可以从数组开头“删掉一段连续的前缀”(前缀长度可以为 0)。要求删除后剩下的部分必须是严格递增的(即剩余数组中任意相
  • 若依框架TagView切换总刷新?别慌,先检查这两个命名规则(附代码示例)
  • 2026年5月国内专业水泥电杆底盘供应商排行:高压水泥电线杆、高强度水泥电杆、高强度水泥电线杆、低压水泥电线杆选择指南 - 优质品牌商家
  • 为 Hermes Agent 框架配置自定义 Taotoken 模型提供商
  • 手把手教你用Python从Excel读取数据,完成K-Means聚类并画出酷炫3D散点图
  • 2026年5月行业观察:莆田可靠的LV鞋店价值评估与供应链选择 - 2026年企业推荐榜
  • 基于ATtiny85的智能烙铁定时器:低成本安全卫士DIY指南
  • 别扔!用吃灰的TP-LINK-WR703N做个无线打印服务器,保姆级刷机教程(含Breed+OpenWrt)