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

数据结构-环形链表

单向环形链表头结点创建

  1. 动态申请内存创建循环链表哨兵头结点;
  2. 内存分配失败,打印提示并返回 NULL;
  3. 将头结点 next 指针指向自身,构造空循环链表;
  4. 返回头结点地址。
node_t *cycle_linklist_create(void) { node_t *head = malloc(sizeof(node_t)); if(head==NULL) { printf("malloc fail\n"); return NULL; } head->next = head; return head; }

单向环形链表头节点后插入新节点

  1. 判断头结点指针是否为 NULL,非法则打印提示并直接返回;
  2. 动态分配新节点,分配失败打印提示并返回;
  3. 指针 p 初始指向原首节点的后继节点;
  4. 给新节点填入数据,新节点后继先指向原来链表第一个有效节点;
  5. 判断原链表为空(new->next == head): 令新节点自环;
  6. 如果链表不为空,p 向后循环遍历找到整条链表尾节点; 将尾节点后继修改为新节点;
  7. 更新头结点后继指针指向新节点,完成循环链表头插。
void cycle_linklist_insert_head(node_t *head,data_t data) { if(head==NULL) { printf("head is NULL\n"); return ; } node_t *new = malloc(sizeof(node_t)); if(new==NULL) { printf("malloc fail\n"); return ; } //记录首节点位置 node_t *p = head->next->next; //新节点存入数据 new->data = data; //新节点获取首节点位置 new->next = head->next; //如果首节点位置为头结点则新节点的下一节点指向自己 if(new->next == head) { new->next = new; }else if(new->next != head) { while(p->next!=head->next) //如果新节点的下一节点不是头结点,则循环到尾节点 p = p->next; //尾节点的下一节点与首节点断开,指向新节点 p->next = new; } head->next = new; //头节点与首节点断开,指向新节点 }

单向环形链表打印链表数据

  1. 判断头结点指针是否为 NULL,非法则打印提示并返回;
  2. 判断循环链表为空,打印空链表提示并返回;
  3. 遍历指针 p 指向链表第一个有效节点;
  4. while 循环条件:当前节点后继不等于原首节点,代表还未到达尾节点; 打印当前节点数据,指针向后移动;
  5. 退出循环时 p 停留在尾节点,单独打印尾节点数据,输出换行。
void print_cycle(node_t *head) { if(head==NULL) { printf("head is NULL\n"); return ; } if(is_empty(head)==0) { printf("is empty\n"); return ; } node_t *p = head->next; while(p->next!= head->next) //循环打印,直到尾节点 { printf("%d ",p->data); p = p->next; } printf("%d\n",p->data); //单独打印尾节点数据 }

单向环形链表按数据内容查找节点

  1. 判断头结点指针是否为 NULL,非法则打印提示并返回 NULL;
  2. 判断循环链表为空,打印空链表提示并返回 NULL;
  3. 遍历指针 p 指向第一个有效节点;
  4. while 循环条件:当前节点后继不等于首有效节点,说明还未到达尾节点; 比对当前节点数据,匹配成功直接返回当前节点地址; 未匹配则 p 向后移动;
  5. 退出循环时 p 停留在尾节点,单独比对尾节点数据;
  6. 尾节点匹配成功返回 p,否则遍历完毕无匹配,返回 NULL。
node_t *cycle_linklist_find_key(node_t *head,data_t key) { if(head==NULL) { printf("head is NULL\n"); return NULL; } if(is_empty(head)==0) { printf("is empty\n"); return NULL; } node_t *p = head->next; while(p->next!= head->next) //同打印数据的思路,先找到尾节点 { if(p->data == key) { return p; } p = p->next; } if(p->data == key) //单独判断尾节点数据是否符合 { return p; }else { return NULL; //都没找到就返回NULL } }

单向环形链表删除首节点

  1. 判断头结点指针是否为 NULL,非法打印提示并返回;
  2. 判断循环链表为空,打印提示并返回;
  3. temp 保存待删除的第一个有效节点;p 用来寻找链表尾节点;
  4. while 循环向后遍历,直到 p 停在尾节点;
  5. 修改头结点后继,指向原首节点的下一个节点;
  6. 修改尾节点的后继,指向新的首节点,维持环形结构;
  7. 释放原首节点内存,完成循环链表头删
void cycle_linklist_delete_head(node_t *head) { if(head==NULL) { printf("head is NULL\n"); return ; } if(is_empty(head)==0) { printf("is empty\n"); return ; } node_t *p = head->next; node_t *temp = head->next; while(p->next != head->next) p = p->next; head->next = temp->next; p->next = head->next; free(temp); }

单向环形链表销毁

  1. 判断二级指针 head 是否为 NULL,参数非法直接返回;
  2. 判断循环链表为空,释放头结点,外部头指针置空后函数返回;
  3. 遍历指针 p 指向第一个有效节点;
  4. while 循环条件:当前节点后继不等于首有效节点 使用 temp 保存当前待释放节点; p 先向后移动; 释放 temp 指向节点;
  5. 循环结束 p 停留在尾节点,单独释放尾节点;
  6. 释放哨兵头结点;
  7. 通过二级指针将外部链表头指针置为 NULL,消除野指针。
void cycle_linklist_destroy(node_t **head) { if(head==NULL) { printf("head is NULL\n"); return ; } if(is_empty(*head)==0) { free(*head); *head = NULL; return ; } node_t *p = (*head)->next; while(p->next != (*head)->next) { node_t *temp = p; p = p->next; free(temp); } free(p); free(*head); *head = NULL; return; }

各函数功能与逻辑梳理

1. cycle_linklist_create — 创建哨兵头结点

  • 动态 malloc 申请头结点内存;分配失败打印信息返回NULL
  • 初始化空环:head->next = head
  • 返回哨兵头地址。

2. cycle_linklist_insert_head — 头插(头结点后插入新节点)

逻辑:新节点成为第一个有效节点

  1. 合法性校验:头指针不能为 NULL;新建节点 malloc 失败直接退出;
  2. 新节点数据赋值,新节点先指向原来首个有效节点;
  3. 区分空链表 / 非空链表:
    • 空链表:处理边界;
    • 非空链表:遍历找到链表尾节点,让尾节点next指向新节点;
  4. 修改哨兵头next指向新节点,完成头插,维持环形闭环。

3. print_cycle — 遍历打印链表

  1. 校验头指针与链表是否为空;
  2. 指针指向首个有效节点;
  3. 循环打印除尾节点以外所有节点;循环结束后单独打印尾节点;

判断依据:p->next != 首有效节点判定未抵达尾部。

4. cycle_linklist_find_key — 根据数值查找节点

  1. 参数合法性、空链表判断;
  2. 从头结点后继开始遍历,循环内依次比对数据;
  3. 循环只遍历到倒数第二个节点,循环结束额外单独校验尾节点;
  4. 找到返回节点地址,查找失败返回NULL

5. cycle_linklist_delete_head — 删除首个有效节点(头删)

  1. 校验头指针、判断链表非空;
  2. 暂存待删除首节点,遍历找到链表尾节点;
  3. 修改哨兵头next指向原第二个有效节点;
  4. 修改尾节点next指向新的首节点,维持环形;
  5. free 释放被删除节点内存。

6. cycle_linklist_destroy — 销毁整条链表(二级指针)

  1. 使用二级指针接收外部头地址,最终可将外部头指针置NULL,防止野指针;
  2. 先释放所有有效节点:循环依次释放直到只剩尾节点,单独释放尾节点;
  3. 释放哨兵头结点;
  4. *head = NULL,清空外部指针。
http://www.jsqmd.com/news/1351247/

相关文章:

  • UE4.27.1 TCP/UDP插件避坑指南:从安装到实战,实现外部通信
  • Unity物体高亮插件QuickOutline:原理、集成与性能优化实战
  • P1564 膜拜【洛谷算法习题】
  • 2026年最新教程:视频号上传视频格式要求怎么转才不踩坑 - 图片处理研究员
  • OpenClaw实战:从零部署AI Agent框架,实现自然语言驱动应用开发
  • 6. 函数上
  • MOSFET结构、参数与驱动电路全解析:从硅基到GaN的开关艺术
  • LangChain / Integrations / Integrations by component / Tool
  • MSVC命令行编译C++程序:从环境配置到构建自动化
  • 数据治理 ROI(上):从成本中心到价值中心,先算清避损账
  • MIT算法导论学习指南:从复杂度分析到AI应用,构建算法思维体系
  • 物联网卡机卡分离无法复机?选对服务商比事后补救更重要
  • 虚实结合调试:基于汇川H5U与FactoryIO的PLC顺序控制实践
  • ROS工作空间与功能包管理最佳实践
  • zynq的stream数据mock和fifo缓冲和同步
  • 如何实现千牛自动提报活动自动化?React Event层注入,表单填充速度碾压人工200倍
  • 嵌入式开发选型指南:CoreMark跑分实测ESP32、STM32与Arduino性能对比
  • AI绘图工具实战指南:从Mermaid到Draw.io,重构技术图表工作流
  • 揭秘无锡网站建设wuxi8878:从草根逆袭到行业标杆的深度访谈与实战指南
  • 2026年乐山旧房翻新市场观察:本土装修企业的工程标准与交付能力解析! - 优质品牌商家
  • MSD-DETR:基于可变形注意力与多尺度融合的工业视觉检测实践
  • UE5蓝图实现游戏角色时间回溯技能:状态记录与还原系统设计
  • JavaScript与Python语法速查表:全栈开发必备
  • 基于Python与耳机麦克风的ASMR音频触发器开发指南
  • Unity小游戏架构选型:MVC与MVVM的实战抉择与避坑指南
  • Steam Economy Enhancer:5分钟掌握Steam市场自动化管理终极指南
  • 解决Vue项目Node.js版本兼容性:从ERR_OSSL_EVP_UNSUPPORTED到构建工具升级
  • 2026 年当下,铜官山有实力的企业AI获客服务商哪家可靠,过去靠业务员跑断腿,如今用它精准锁客,竟省了大半获客成本?-抖盈企服 - 行业鉴选官
  • 告别文档地狱:Apifox接口文档自动化生成与团队协作实战指南
  • Isaac Lab Arena 全身机器人机动与操控工作流实战指南