单向环形链表头结点创建
-
动态申请内存创建循环链表哨兵头结点;
-
内存分配失败,打印提示并返回 NULL;
-
将头结点 next 指针指向自身,构造空循环链表;
-
返回头结点地址。
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;}
单向环形链表头节点后插入新节点
-
判断头结点指针是否为 NULL,非法则打印提示并直接返回;
-
动态分配新节点,分配失败打印提示并返回;
-
指针 p 初始指向原首节点的后继节点;
-
给新节点填入数据,新节点后继先指向原来链表第一个有效节点;
-
判断原链表为空(
new->next == head): 令新节点自环; -
如果链表不为空,p 向后循环遍历找到整条链表尾节点; 将尾节点后继修改为新节点;
-
更新头结点后继指针指向新节点,完成循环链表头插。
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; //头节点与首节点断开,指向新节点}
单向环形链表打印链表数据
-
判断头结点指针是否为 NULL,非法则打印提示并返回;
-
判断循环链表为空,打印空链表提示并返回;
-
遍历指针 p 指向链表第一个有效节点;
-
while 循环条件:当前节点后继不等于原首节点,代表还未到达尾节点; 打印当前节点数据,指针向后移动;
-
退出循环时 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); //单独打印尾节点数据}
单向环形链表按数据内容查找节点
-
判断头结点指针是否为 NULL,非法则打印提示并返回 NULL;
-
判断循环链表为空,打印空链表提示并返回 NULL;
-
遍历指针 p 指向第一个有效节点;
-
while 循环条件:当前节点后继不等于首有效节点,说明还未到达尾节点; 比对当前节点数据,匹配成功直接返回当前节点地址; 未匹配则 p 向后移动;
-
退出循环时 p 停留在尾节点,单独比对尾节点数据;
-
尾节点匹配成功返回 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 }}
单向环形链表删除首节点
-
判断头结点指针是否为 NULL,非法打印提示并返回;
-
判断循环链表为空,打印提示并返回;
-
temp 保存待删除的第一个有效节点;p 用来寻找链表尾节点;
-
while 循环向后遍历,直到 p 停在尾节点;
-
修改头结点后继,指向原首节点的下一个节点;
-
修改尾节点的后继,指向新的首节点,维持环形结构;
-
释放原首节点内存,完成循环链表头删
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);}
单向环形链表销毁
-
判断二级指针 head 是否为 NULL,参数非法直接返回;
-
判断循环链表为空,释放头结点,外部头指针置空后函数返回;
-
遍历指针 p 指向第一个有效节点;
-
while 循环条件:当前节点后继不等于首有效节点 使用 temp 保存当前待释放节点; p 先向后移动; 释放 temp 指向节点;
-
循环结束 p 停留在尾节点,单独释放尾节点;
-
释放哨兵头结点;
-
通过二级指针将外部链表头指针置为 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 --- 头插(头结点后插入新节点)
逻辑:新节点成为第一个有效节点
- 合法性校验:头指针不能为 NULL;新建节点 malloc 失败直接退出;
- 新节点数据赋值,新节点先指向原来首个有效节点;
- 区分空链表 / 非空链表:
- 空链表:处理边界;
- 非空链表:遍历找到链表尾节点,让尾节点
next指向新节点;
- 修改哨兵头
next指向新节点,完成头插,维持环形闭环。
3. print_cycle --- 遍历打印链表
- 校验头指针与链表是否为空;
- 指针指向首个有效节点;
- 循环打印除尾节点以外所有节点;循环结束后单独打印尾节点;
判断依据:
p->next != 首有效节点判定未抵达尾部。
4. cycle_linklist_find_key --- 根据数值查找节点
- 参数合法性、空链表判断;
- 从头结点后继开始遍历,循环内依次比对数据;
- 循环只遍历到倒数第二个节点,循环结束额外单独校验尾节点;
- 找到返回节点地址,查找失败返回
NULL。
5. cycle_linklist_delete_head --- 删除首个有效节点(头删)
- 校验头指针、判断链表非空;
- 暂存待删除首节点,遍历找到链表尾节点;
- 修改哨兵头
next指向原第二个有效节点; - 修改尾节点
next指向新的首节点,维持环形; - free 释放被删除节点内存。
6. cycle_linklist_destroy --- 销毁整条链表(二级指针)
- 使用二级指针接收外部头地址,最终可将外部头指针置
NULL,防止野指针; - 先释放所有有效节点:循环依次释放直到只剩尾节点,单独释放尾节点;
- 释放哨兵头结点;
*head = NULL,清空外部指针。