尾不指头:一种别致的单向循环链表
单向循环链表操作实现
单向循环链表是一种链表数据结构,和单链表不同的地方是:最后一个节点的指针不指向 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给新结点内存分配失败会返回NULL
return 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给新结点内存分配失败会返回NULL
if (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); // 头插 10
Circl_HeadInsert(Head, 20); // 头插 20
Circll_TailInsert(Head, 30); // 尾插 30
Circll_TailInsert(Head, 40); // 尾插 40
Circll_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仅作为入口指针和结构管理存在,不参与数据环。
无论采用哪种方式,只要在整个代码中保持统一逻辑,应该都是合理的实现。

浙公网安备 33010602011771号