数据结构-环形链表

单向环形链表头结点创建

  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,清空外部指针。
相关推荐
萧瑟余晖3 小时前
Java深入解析篇十七之SpringCloud
java·开发语言
是未才3 小时前
从输入 URL 到页面返回:DNS、路由、TLS 与 HTTP 完整链路
java·后端·计算机网络
ttod_qzstudio3 小时前
Java 常用语法极简通关(五):类与对象——字段、方法、构造器、this 与 static
java·开发语言·python
萧瑟余晖4 小时前
Java深入解析篇十七之Spring Security
java·开发语言·spring
爱跳舞的烤冷面5 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
MC皮蛋侠客7 小时前
Redis 系列(一):全景与最小闭环——从 `SET` 命令到内存数据结构
数据结构·数据库·redis
疯狂打码的少年7 小时前
【数据结构】队列:定义、顺序队列与链式队列
数据结构·笔记
离陌在学C#7 小时前
C# 重载与重写:深入理解面向对象编程的核心概念
java·c#
洛阳泰山7 小时前
AI 应用层被 Python 卷成红海,为什么我偏要用 Java 造一个 RAG + 工作流引擎?
java·人工智能·后端