单向循环链表操作实现
单向循环链表是一种链表数据结构,和单链表不同的地方是:最后一个节点的指针不指向 null
,而是指向链表的头节点,形成一个环状结构。这样从任何一个节点出发,都可以通过指针遍历整个链表。
代码实现
单向循环链表的创建、插入、删除、查找等常用操作。
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>typedef int DataType_t;
typedef struct circularlink_manager
{DataType_t data; struct circularlink_manager *next;}Circllink_t;//用来创建一个头结点 Circllink_t* Circllink_Create(void)
{Circllink_t *Head = (Circllink_t *)calloc(1,sizeof(Circllink_t));if(Head == NULL){perror("Calloc memory for the Head is failed!\n");exit(-1);}//链表只有一个头结点时,头结点next指向自身Head->next = Head;return Head;
}//用来创建一个新的有效结点
Circllink_t* Circllink_NewNode(DataType_t data)
{Circllink_t *New = (Circllink_t *)calloc(1,sizeof(Circllink_t));if(New == NULL){perror("Calloc memory for the Head is failed!\n");return NULL;}//新结点初始化New->data = data;New->next = NULL;return New;
}//头插(在头结点后插入一个结点)
bool Circl_HeadInsert(Circllink_t* Head,DataType_t data)
{Circllink_t *New = Circllink_NewNode(data);if (New == NULL){// 函数Circllink_NewNode给新结点内存分配失败会返回NULLreturn false; }if(Head->next == Head){//如果只有头结点,New直接成为首结点Head->next = New;New ->next = New;}else{Circllink_t *Find_Tail = Head->next;//定义指针Tail,找到并指向最后一个结点while(Find_Tail->next != Head->next){Find_Tail = Find_Tail->next;}New ->next = Head->next;Head->next = New;//更新尾结点的next,尾结点next始终指向首结点Find_Tail->next = New;
}return true;
}
//尾插
bool Circll_TailInsert(Circllink_t* Head,DataType_t data)
{Circllink_t *New = Circllink_NewNode(data);// 函数Circllink_NewNode给新结点内存分配失败会返回NULLif (New == NULL) {return false; }//如果只有头结点,New直接成为首结点if(Head->next == Head){Head->next = New;New ->next = New;}else{Circllink_t *Find_Tail = Head->next;while(Find_Tail->next != Head->next){//循环找链表最后一个结点Find_Tail = Find_Tail->next;}Find_Tail->next = New;New ->next = Head->next;}return true;
}
//指定链表位置插入结点
bool Circll_DestInsert(Circllink_t* Head,DataType_t dest,DataType_t data)
{Circllink_t *New = Circllink_NewNode(data);if (New == NULL) {return false; }if(Head->next == Head){Head->next = New;New ->next = New;return true;}Circllink_t *Find_Dest = Head->next;//遍历链表,找到数据域data等于目标值dest的结点while(Find_Dest->data != dest){ if (Find_Dest->next == Head->next){//若遍历到链表最后一个结点,data和dest还不相等,说明dest为无效指定结点位置printf("无效指定结点位置\n");return false;}Find_Dest = Find_Dest->next;}if (Find_Dest->next == Head->next){//若遍历到链表最后一个结点,数据域data等于dest,新结点New成为尾结点Find_Dest->next = New;New->next = Head->next;}else{New->next = Find_Dest->next;Find_Dest->next = New;}return true;
}//遍历链表
bool CirLList_Print(Circllink_t *Head)
{Circllink_t *Phead = Head;if(Head->next == Head){printf("current circulinked is empty!\n");return false;}while(Phead->next){Phead = Phead->next;printf("data = %d\n",Phead->data);if(Phead->next == Head->next){ break;}}return true;
}//删除链表头结点
bool CirLList_HeadDel(Circllink_t *Head)
{Circllink_t *Phead = Head;Circllink_t *Temp = Head->next;if(Head->next == Head){printf("current circulinked is empty!\n");return false;}//如果链表只有一个有效结点if(Head->next->next == Head->next){Head->next = Head;free(Temp);return true;}//找到最后一个结点while(Phead->next){Phead = Phead->next;if(Phead->next == Head->next){ break;}}Phead ->next = Head->next->next;//更新尾结点next的指向Head ->next = Phead->next;Temp ->next = NULL;free(Temp);return true;
}
//删除链表尾结点
bool CirLList_TailDel(Circllink_t *Head)
{Circllink_t *Phead = Head->next;if(Head->next == Head){printf("current circulinked is empty!\n");return false;}if(Phead->next == Head->next){//如果链表只有一个首结点(有效结点)Head->next = Head;free(Phead);Phead = NULL;return true;}while(Phead->next->next != Head->next){//找到最后一个结点Phead = Phead->next;}free(Phead->next);Phead->next = Head->next;//更新尾结点next指向return true;
}
//指定位置删除链表结点
bool cirlist_del_dest(Circllink_t* Head,DataType_t dest)
{ if(Head->next == Head){printf("current circulinked is empty!\n");return false;}Circllink_t *PreFind_Dest = Head;Circllink_t *Find_Dest = Head->next;while(Find_Dest->data != dest){ if (Find_Dest->next == Head->next){//若遍历到链表最后一个结点printf("未找到指定节点\n");return false;}PreFind_Dest = PreFind_Dest->next;Find_Dest = Find_Dest->next;}//若目标结点是唯一结点if (Find_Dest == Find_Dest->next){Head->next = Head;}else{//PreFind_Dest->next = Find_Dest->next;if(Find_Dest == Head->next){Circllink_t *Find_Tail = Head->next;while(Find_Tail->next != Head->next){//循环找链表最后一个结点Find_Tail = Find_Tail->next;}Find_Tail->next = Find_Dest->next;PreFind_Dest->next = Find_Dest->next;}else{PreFind_Dest->next = Find_Dest->next; }}free(Find_Dest);return true;
}
主函数测试
int main(int argc, char const *argv[])
{Circllink_t *Head = Circllink_Create();printf("链表已创建\n");// 测试插入操作printf("\n=== 插入测试 ===\n");Circl_HeadInsert(Head, 10); // 头插 10Circl_HeadInsert(Head, 20); // 头插 20Circll_TailInsert(Head, 30); // 尾插 30Circll_TailInsert(Head, 40); // 尾插 40Circll_DestInsert(Head, 30, 35); // 在 30 后插入 35// 打印链表printf("\n插入后的链表:\n");CirLList_Print(Head);//预估结果:20,10 30 35 40// 测试删除操作printf("\n=== 删除测试 ===\n");printf("删除指定值 20 的节点:\n");if (cirlist_del_dest(Head, 20)) {printf("删除成功\n");} else {printf("删除失败\n");}CirLList_Print(Head);printf("\n删除头节点:\n");if (CirLList_HeadDel(Head)) {printf("删除成功\n");} else {printf("删除失败\n");}CirLList_Print(Head);printf("\n删除尾节点:\n");if (CirLList_TailDel(Head)) {printf("删除成功\n");} else {printf("删除失败\n");}// 再次打印链表printf("\n删除后的链表:\n");CirLList_Print(Head);//预估结果:30 35 // 释放头节点free(Head);printf("\n链表已释放\n");return 0;
}
测试结果
细心的读者可能会发现:在本实现中,链表的最后一个有效节点的
next
指向的是第一个有效节点(即Head->next
),而不是头结点Head
本身。这与部分教材中“尾节点指向头结点”的描述略有不同。✅设计思想:头结点是“管理节点”,不参与数据循环
在我的实现中,头结点(
Head
)被定义为一个纯管理性质的辅助节点,它不存储任何有效数据,也不应被纳入“数据节点”的循环逻辑中。真正的数据循环只发生在所有有效数据节点之间。
尾节点的
next
指向第一个有效节点(Head->next
),形成一个仅包含有效数据节点的闭环。头结点
Head
仅作为入口指针和结构管理存在,不参与数据环。
无论采用哪种方式,只要在整个代码中保持统一逻辑,应该都是合理的实现。