news 2026/5/8 15:33:47

嵌入式 - 数据结构与算法:(1-5)数据结构 - 单向循环链表(Circular Linked List)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
嵌入式 - 数据结构与算法:(1-5)数据结构 - 单向循环链表(Circular Linked List)
上一篇下一篇
单链表的两个核心缺点

目 录

  • 单向循环链表(Circular Linked List)
    • 1)什么是循环链表?
    • 2)常用操作
      • 2.1)初始化空循环链表
      • 2.2)在末尾插入新节点
      • 2.3)删除第一个值为 target 的节点
      • 2.4)打印整个循环链表(避免死循环!)
    • 3)完整示例程序

单向循环链表(Circular Linked List)

1)什么是循环链表?

循环链表是单链表的一种变形,其特点是:最后一个节点的next指针不再指向NULL,而是指向链表的第一个节点(或头节点),从而形成一个闭环结构(其余结构和操作方法和单链表很类似,只是为尾节点判断方式变了)。

主要特点:

  • 无真正“尾部”:从任意节点出发,沿next指针遍历,最终都会回到起点
  • 空链表判断特殊:若使用带头节点的结构,空表满足head->next == head
  • 适合循环场景:如约瑟夫问题、轮询调度、环形缓冲区等

2)常用操作

假设采用带哨兵头节点的单向循环链表(头节点不存有效数据,仅作起始标记),空表时head->next = head

2.1)初始化空循环链表

/** * @brief: initCircularList —— 创建并初始化一个带头节点的空循环链表 * @return: 指向头节点的指针;失败时返回 NULL * @note: 头节点 data 无意义,其 next 指向自身表示空表。 */ListNode*initCircularList(){ListNode*head=(ListNode*)malloc(sizeof(ListNode));if(head==NULL)returnNULL;// 没有空间,造成分配内存失败head->next=head;// 关键:空循环链表中,头节点指向自己returnhead;}

2.2)在末尾插入新节点

/** * @brief: insertAtTail —— 在循环链表尾部插入新节点 * @param[in,out]: head - 链表头指针(不可为 NULL) * @param[in]: value - 要插入的数据 * @return: 成功返回 true,失败返回 false * @note: 尾部即头节点的前一个位置。时间复杂度 O(n)。 */boolinsertAtTail(ListNode*head,intvalue){if(head==NULL)returnfalse;// 创建新节点ListNode*newNode=(ListNode*)malloc(sizeof(ListNode));if(newNode==NULL)returnfalse;newNode->data=value;// 找到当前尾节点(即 head 的前驱)ListNode*tail=head;while(tail->next!=head){tail=tail->next;}// 插入新节点tail->next=newNode;newNode->next=head;// 新尾节点指向头,保持循环returntrue;}

2.3)删除第一个值为 target 的节点

/** * @brief: deleteByValue —— 删除循环链表中第一个值等于 target 的节点 * @param[in,out]: head - 链表头指针(不可为 NULL) * @param[in]: target - 要删除的目标值 * @return: 成功删除返回 true,未找到返回 false * @note: 若删除后链表为空,则 head->next 仍指向 head。 */booldeleteByValue(ListNode*head,inttarget){if(head==NULL||head->next==head){returnfalse;// 空表}ListNode*prev=head;ListNode*current=head->next;// 遍历直到回到 headwhile(current!=head){if(current->data==target){prev->next=current->next;free(current);returntrue;}prev=current;current=current->next;}returnfalse;// 未找到}

2.4)打印整个循环链表(避免死循环!)

/** * @brief: printCircularList —— 安全打印循环链表内容 * @param[in]: head - 链表头指针 * @return: 链表长度(不含头节点) * @note: 从 head->next 开始,遇到 head 停止,防止无限循环。 */intprintCircularList(ListNode*head){if(head==NULL||head->next==head){printf("循环链表为空。\n");return0;}printf("循环链表: ");ListNode*current=head->next;intcount=0;while(current!=head){printf("%d -> ",current->data);current=current->next;count++;}printf("(回到头)\n");returncount;}

3)完整示例程序

#include<stdio.h>#include<stdlib.h>#include<stdbool.h>// 定义链表节点结构typedefstructListNode{intdata;structListNode*next;}ListNode;/** * @brief: initCircularList —— 创建并初始化一个带头节点的空循环链表 * @return: 指向头节点的指针;失败时返回 NULL * @note: 头节点 data 无意义,其 next 指向自身表示空表。 */ListNode*initCircularList(){ListNode*head=(ListNode*)malloc(sizeof(ListNode));if(head==NULL)returnNULL;head->next=head;// 关键:空循环链表中,头节点指向自己returnhead;}/** * @brief: insertAtTail —— 在循环链表尾部插入新节点 * @param[in,out]: head - 链表头指针(不可为 NULL) * @param[in]: value - 要插入的数据 * @return: 成功返回 true,失败返回 false * @note: 尾部即头节点的前一个位置。时间复杂度 O(n)。 */boolinsertAtTail(ListNode*head,intvalue){if(head==NULL)returnfalse;ListNode*newNode=(ListNode*)malloc(sizeof(ListNode));if(newNode==NULL)returnfalse;newNode->data=value;// 找到当前尾节点(即 head 的前驱)ListNode*tail=head;while(tail->next!=head){tail=tail->next;}// 插入新节点tail->next=newNode;newNode->next=head;// 新尾节点指向头,保持循环returntrue;}/** * @brief: deleteByValue —— 删除循环链表中第一个值等于 target 的节点 * @param[in,out]: head - 链表头指针(不可为 NULL) * @param[in]: target - 要删除的目标值 * @return: 成功删除返回 true,未找到返回 false * @note: 若删除后链表为空,则 head->next 仍指向 head。 */booldeleteByValue(ListNode*head,inttarget){if(head==NULL||head->next==head){returnfalse;// 空表}ListNode*prev=head;ListNode*current=head->next;// 遍历直到回到 headwhile(current!=head){if(current->data==target){prev->next=current->next;free(current);returntrue;}prev=current;current=current->next;}returnfalse;// 未找到}/** * @brief: printCircularList —— 安全打印循环链表内容 * @param[in]: head - 链表头指针 * @return: 链表长度(不含头节点) * @note: 从 head->next 开始,遇到 head 停止,防止无限循环。 */intprintCircularList(ListNode*head){if(head==NULL||head->next==head){printf("循环链表为空。\n");return0;}printf("循环链表: ");ListNode*current=head->next;intcount=0;while(current!=head){printf("%d -> ",current->data);current=current->next;count++;}printf("(回到头)\n");returncount;}/** * @brief: destroyCircularList —— 销毁整个循环链表并释放内存 * @param[in,out]: head - 链表头指针的地址 * @return: 无 * @note: 释放所有节点后将头指针置为 NULL。 */voiddestroyCircularList(ListNode**headRef){if(headRef==NULL||*headRef==NULL)return;ListNode*head=*headRef;if(head->next==head){// 空表,只释放头节点free(head);*headRef=NULL;return;}// 从第一个有效节点开始释放ListNode*current=head->next;while(current!=head){ListNode*next=current->next;free(current);current=next;}// 最后释放头节点free(head);*headRef=NULL;}// 主函数:完整示例intmain(){// 1. 初始化空循环链表ListNode*head=initCircularList();if(head==NULL){printf("初始化失败!\n");return-1;}printf("=== 初始化空链表 ===\n");printCircularList(head);// 2. 插入元素printf("\n=== 插入元素 10, 20, 30 ===\n");insertAtTail(head,10);insertAtTail(head,20);insertAtTail(head,30);intlen=printCircularList(head);printf("链表长度: %d\n",len);// 3. 删除中间元素printf("\n=== 删除元素 20 ===\n");if(deleteByValue(head,20)){printf("删除成功!\n");}else{printf("未找到要删除的元素。\n");}printCircularList(head);// 4. 删除不存在的元素printf("\n=== 尝试删除不存在的元素 99 ===\n");if(deleteByValue(head,99)){printf("删除成功!\n");}else{printf("未找到要删除的元素。\n");}printCircularList(head);// 5. 清理内存printf("\n=== 销毁链表 ===\n");destroyCircularList(&head);if(head==NULL){printf("链表已成功销毁。\n");}return0;}

运行结果为

=== 初始化空链表 === 循环链表为空。 === 插入元素 10, 20, 30 === 循环链表: 10 -> 20 -> 30 -> (回到头) 链表长度: 3 === 删除元素 20 === 删除成功! 循环链表: 10 -> 30 -> (回到头) === 尝试删除不存在的元素 99 === 未找到要删除的元素。 循环链表: 10 -> 30 -> (回到头) === 销毁链表 === 链表已成功销毁。

提示:实际应用中,若频繁在尾部操作,可使用尾指针(rear)代替头指针,使尾部插入变为 O(1)。此时空表条件为rear->next == rear


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/8 15:33:09

在Mac上原生运行iOS游戏:PlayCover终极指南与性能优化技巧

在Mac上原生运行iOS游戏&#xff1a;PlayCover终极指南与性能优化技巧 【免费下载链接】PlayCover Community fork of PlayCover 项目地址: https://gitcode.com/gh_mirrors/pl/PlayCover 想象一下&#xff0c;在Mac的大屏幕上流畅运行《原神》《崩坏&#xff1a;星穹铁…

作者头像 李华
网站建设 2026/5/8 15:32:45

如何利用Chatbox高效管理你的AI对话?5大核心功能解密

如何利用Chatbox高效管理你的AI对话&#xff1f;5大核心功能解密 【免费下载链接】chatbox Powerful AI Client 项目地址: https://gitcode.com/GitHub_Trending/ch/chatbox 在AI助手日益普及的今天&#xff0c;你是否曾为对话记录丢失而烦恼&#xff1f;精心构思的技术…

作者头像 李华
网站建设 2026/5/8 15:32:15

黑龙江省考笔试机构怎么选?行测申论课程 + 服务完整评测指南

一、为什么一定要重视黑龙江省考机构选型近些年黑龙江省考笔试竞争逐年白热化&#xff0c;行测、申论命题风格、本地素材权重和全国联考差异极大。很多考生选机构容易踩三大坑&#xff1a;课程不贴合黑龙江本地考情&#xff0c;学的通用内容用不上&#xff1b;宣传服务天花乱坠…

作者头像 李华
网站建设 2026/5/8 15:32:01

对接Claude Code编程助手避免封号与Token不足

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 对接Claude Code编程助手避免封号与Token不足 对于使用Claude Code进行编程辅助的开发者而言&#xff0c;直接使用原厂服务有时会面…

作者头像 李华
网站建设 2026/5/8 15:31:57

机器学习实战:从过拟合、特征工程到模型部署的完整指南

1. 项目概述&#xff1a;一个机器学习问答库的诞生与价值在机器学习和人工智能领域摸爬滚打了十几年&#xff0c;我见过太多重复的问题&#xff0c;也踩过无数相似的坑。很多初学者&#xff0c;甚至是有一定经验的从业者&#xff0c;常常会卡在一些看似基础&#xff0c;实则微妙…

作者头像 李华