数据结构-环形链表

单向环形链表头结点创建

  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;

    }

各函数功能与逻辑梳理

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

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

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

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

  1. 参数合法性、空链表判断;
  2. 从头结点后继开始遍历,循环内依次比对数据;
  3. 循环只遍历到倒数第二个节点,循环结束额外单独校验尾节点;
  4. 找到返回节点地址,查找失败返回NULL
  1. 校验头指针、判断链表非空;
  2. 暂存待删除首节点,遍历找到链表尾节点;
  3. 修改哨兵头next指向原第二个有效节点;
  4. 修改尾节点next指向新的首节点,维持环形;
  5. free 释放被删除节点内存。
  1. 使用二级指针接收外部头地址,最终可将外部头指针置NULL,防止野指针;
  2. 先释放所有有效节点:循环依次释放直到只剩尾节点,单独释放尾节点;
  3. 释放哨兵头结点;
  4. *head = NULL,清空外部指针。
相关推荐
用户3126874877202 小时前
ConcurrentHashMap 怎么保证线程安全?从分段锁到 CAS+synchronized
java
vipxieliang2 小时前
ValidX vs Apache Commons Validator:功能与性能对比
java·spring boot
SimonKing2 小时前
升级Spring Boot 4后,从 Jackson 2 到 3,到底有哪些变化
java·后端·程序员
吃饱了得干活3 小时前
一篇讲清楚Spring Boot:自动装配、启动器、过滤器、拦截器、设计模式
java·spring boot·后端
lhldsg4 小时前
AI零售系统实战指南:从架构设计到落地部署全解析
java·人工智能·小程序·uni-app·零售
Interview Aid1124 小时前
TikTok OA 四题分享|半小时内 AC,题目基本都是实现题
java·开发语言·算法·面试·职场和发展
mldong5 小时前
换了工作流引擎,前端一行代码没改
java·架构
微尘寒风12 小时前
【Git】的安装和使用
java·git
y = xⁿ13 小时前
DeepSeek Harness 学习日记:关于Agent接口,工具调用的底层实现
android·java·学习
侧耳倾听11113 小时前
jwt使用简介
java·jwt