单向循环链表(测试通过)
/*************************************************
*
* file name:LkList.c
* author :momolyl@126.com
* date :2024/04/23
* brief :通过构建单向循环链表学习顺序存储
* note :None
*
* CopyRight (c) 2024 momolyl@126.com All Right Reseverd
*
**************************************************/
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
/*************************************************
*
* func name :
* brief :
* func parameter:
*
*
* return :None
* note :None
* func author :momolyl@126.com
* date :2024/04/23
* version :V1.0
**************************************************/
// 结构体用于构建链表节点(数据域+指针域)
typedef int Datatype_t;
typedef struct CirLkList
{
Datatype_t Data;
struct CirLkList *Next;
} CriLkList_t;
// 创建一个空链表,使用头节点
CriLkList_t *CirLkList_Create(void)
{
CriLkList_t *Head = (CriLkList_t *)calloc(1, sizeof(CriLkList_t)); // 为头节点申请内存,并错误处理
if (NULL == Head)
{
perror("calloc memory for CirLkList if failed!\n");
exit(-1);
}
Head->Next = Head; // 将头节点的指针域指向Head,体现循环链表的特性
return Head; // 返回头节点的地址
}
// 创建一个新节点,并对新节点初始化(数据域+指针域)
CriLkList_t *CirLkList_NewNode(Datatype_t data)
{
CriLkList_t *NewNode = (CriLkList_t *)calloc(1, sizeof(CriLkList_t)); // 为新节点申请一个地址,
if (NULL == NewNode) // 申请新节点的错误处理
{
perror("calloc memory for NewNode if failed!\n");
exit(-1);
}
NewNode->Data = data; // 初始化数据域
NewNode->Next = NULL; // 初始化指针域
return NewNode;
}
// 向链表中头插一个新节点
void CirLkList_HeadAdd(CriLkList_t *Head, Datatype_t data)
{
CriLkList_t *NewNode = CirLkList_NewNode(data);
if (Head == Head->Next) // 判断链表是否为空
{
// 链表为空
Head->Next = NewNode;
NewNode->Next = NewNode;
return;
}
// 2.2链表非空
{
CriLkList_t *Phead = Head->Next; // 备份首结点的地址
// 遍历找到尾结点
while (Phead->Next != Head->Next) // 判断条件为:当前结点的Next指针是否指向首结点
{
Phead = Phead->Next;
}
Phead->Next = NewNode;
NewNode->Next = Head->Next;
Head->Next = NewNode;
}
}
// 向链表中尾插一个新节点
void CirLkList_TailAdd(CriLkList_t *Head, Datatype_t data)
{
CriLkList_t *NewNode = CirLkList_NewNode(data);
if (Head == Head->Next) // 判断链表是否为空
{
// 链表为空
Head->Next = NewNode;
NewNode->Next = NewNode;
return;
}
// 2.2链表非空
CriLkList_t *Phead = Head->Next; // 备份首结点的地址
// 遍历找到尾结点
while (Phead->Next != Head->Next) // 判断条件为:当前结点的Next指针是否指向首结点
{
Phead = Phead->Next; // Phead在循环结束时代表尾结点的地址
}
// 在尾端插入新节点
Phead->Next = NewNode;
NewNode->Next = Head->Next;
}
// 向链表中指定数据结点后插入一个新节点
void CirLkList_AppointAdd(CriLkList_t *Head, Datatype_t DestVal, Datatype_t data)
{
CriLkList_t *NewNode = CirLkList_NewNode(data);
if (Head == Head->Next) // 判断链表是否为空
{
// 链表为空
Head->Next = NewNode;
NewNode->Next = NewNode;
return;
}
// 2.2链表非空
CriLkList_t *Phead = Head->Next; // 备份首结点的地址,用于遍历找到目标结点的地址
CriLkList_t *Dest = NULL; // 用于判断是否成功查找到指定数值的结点
// 遍历找到目标结点
while (Phead->Data != DestVal) // 判断条件:当前结点的数据域的数据是否与目标值不同
{
Phead = Phead->Next; // 不同则偏移
if (Phead->Next == Head->Next && Phead->Data != DestVal) // 判断条件:到达尾结点且未找到目标值则退出循环
{
Dest = Phead; // 如果未找到目标值,则改Dest的值
break;
}
}
if (Dest) // 如果未找到目标值
{
printf("Can find the DestVal %d!\n", DestVal);
return;
Phead->Next = NewNode;
NewNode->Next = Head->Next;
}
// 如果找到目标值
NewNode->Next = Phead->Next;
Phead->Next = NewNode;
}
// 在链表中头删一个节点
bool CirLkList_HeadDel(CriLkList_t *Head)
{
// 判断链表是否为空
if (Head == Head->Next) // 头节点的Next指针指向头节点的地址时,链表为空
return false; // 为空则删除失败
CriLkList_t *Phead1 = Head->Next; // 备份首节点地址,用于遍历查找尾结点
CriLkList_t *Phead2 = Head->Next; // 备份首节点地址,用于最后释放首结点地址
// 遍历查找尾结点
while (Phead1->Next != Head->Next) // 判断条件为:当前结点的Next指针是否指向首结点
{
Phead1 = Phead1->Next; // Phead1在循环结束时代表尾结点的地址
}
Phead1->Next = Head->Next->Next; // 将尾结点的Next指针指向首结点的直接后继结点
Head->Next = Head->Next->Next; // 将头结点的Next指针指向首结点的直接后继结点
Phead2->Next = NULL; // 将首结点的Next指针指向NULL(即断开与其直接后继结点的链接)
free(Phead2);
return true;
}
// 在链表中尾删一个节点
bool CirLkList_TailDel(CriLkList_t *Head)
{
// 判断链表是否为空
if (Head == Head->Next) // 头节点的Next指针指向头节点的地址时,链表为空
return false; // 为空则删除失败
CriLkList_t *Phead1 = Head->Next; // 备份首节点地址,用于遍历查找尾结点
CriLkList_t *Phead2 = Head; // 备份头节点地址,用于遍历查找尾结点的直接前驱结点
// 遍历查找尾结点
while (Phead1->Next != Head->Next) // 判断条件为:当前结点的Next指针是否指向首结点
{
Phead1 = Phead1->Next; // Phead1在循环结束时代表尾结点的地址
Phead2 = Phead2->Next; // Phead2在循环结束时代表尾结点的直接前驱结点的地址
}
Phead2->Next = Head->Next; // 将尾结点的直接前驱结点的Next指针指向首结点
Phead1->Next = NULL; // 将尾结点Next指针指向NULL
free(Phead1);
return true;
}
// 在链表中删指定数据结点
bool CirLkList_AppointDel(CriLkList_t *Head, Datatype_t DestVal)
{
// 判断链表是否为空
if (Head == Head->Next) // 头节点的Next指针指向头节点的地址时,链表为空
return false; // 为空则删除失败
CriLkList_t *Phead1 = Head->Next; // 备份首结点的地址,用于遍历目标节点
CriLkList_t *Phead2 = Head; // 备份头结点的地址,用于遍历目标节点的前驱结点
CriLkList_t *Dest = NULL; // 用于判断是否成功查找到指定数值的结点
// 遍历找到指定数据结点
// 遍历找到目标结点
while (Phead1->Data != DestVal) // 判断条件:当前结点的数据域的数据是否与目标值不同
{
Phead1 = Phead1->Next; // 不同则指向目标结点的指针偏移
Phead2 = Phead2->Next; // 不同则指向目标结点的直接前驱的指针偏移
if (Phead1->Next == Head->Next && Phead1->Data != DestVal) // 判断条件:到达尾结点且未找到目标值则退出循环
{
Dest = Phead1; // 如果未找到目标值,则改Dest的值
break;
}
}
if (Dest) // 如果没有找到指定数值,则删除失败
{
printf("Can find the DestVal %d!\n", DestVal);
return false;
}
else // 如果成功查找到指定数值,则删除指定数据结点
{
// 1.目标结点为头节点(使用头删函数的代码)
if (Phead1 == Head->Next)
{
CriLkList_t *Phead3 = Head->Next; // 备份首节点地址,用于遍历查找尾结点
CriLkList_t *Phead4 = Head->Next; // 备份首节点地址,用于最后释放首结点地址
// 遍历查找尾结点
while (Phead3->Next != Head->Next) // 判断条件为:当前结点的Next指针是否指向首结点
{
Phead3 = Phead3->Next; // Phead1在循环结束时代表尾结点的地址
}
Phead3->Next = Head->Next->Next; // 将尾结点的Next指针指向首结点的直接后继结点
Head->Next = Head->Next->Next; // 将头结点的Next指针指向首结点的直接后继结点
Phead4->Next = NULL; // 将首结点的Next指针指向NULL(即断开与其直接后继结点的链接)
free(Phead4);
}
// 2.目标结点为尾节点(使用尾删函数的代码)
else if (Phead1->Next == Head->Next)
{
CriLkList_t *Phead3 = Head->Next; // 备份首节点地址,用于遍历查找尾结点
CriLkList_t *Phead4 = Head; // 备份头节点地址,用于遍历查找尾结点的直接前驱结点
// 遍历查找尾结点
while (Phead3->Next != Head->Next) // 判断条件为:当前结点的Next指针是否指向首结点
{
Phead3 = Phead3->Next; // Phead1在循环结束时代表尾结点的地址
Phead4 = Phead4->Next; // Phead2在循环结束时代表尾结点的直接前驱结点的地址
}
Phead4->Next = Head->Next; // 将尾结点的直接前驱结点的Next指针指向首结点
Phead3->Next = NULL; // 将尾结点Next指针指向NULL
free(Phead3);
}
// 3.目标结点在中间
else
{
Phead2->Next = Phead1->Next;
Phead1->Next = NULL;
free(Phead1);
}
return true;
}
}
void Test(CriLkList_t *Head)
{
CriLkList_t *Phead = Head->Next; // 备份首节点的地址,用于遍历结点
while (Phead->Next != Head->Next) // 遍历到尾结点
{
printf("%d ", Phead->Data);
Phead = Phead->Next;
}
printf("%d\n", Phead->Data); // 打印尾结点的数据
}
int main(void)
{
CriLkList_t *Head = CirLkList_Create();
// 头插
CirLkList_HeadAdd(Head, 10);
CirLkList_HeadAdd(Head, 20);
CirLkList_HeadAdd(Head, 30);
CirLkList_HeadAdd(Head, 40);
CirLkList_HeadAdd(Head, 50);
Test(Head);
// 尾插
CirLkList_TailAdd(Head, 1);
CirLkList_TailAdd(Head, 2);
CirLkList_TailAdd(Head, 3);
CirLkList_TailAdd(Head, 4);
CirLkList_TailAdd(Head, 5);
Test(Head);
// 指定插
CirLkList_AppointAdd(Head, 5, 88);
// CirLkList_AppointAdd(Head, 30, 35);
Test(Head);
// return 0;
// 头删
CirLkList_HeadDel(Head);
CirLkList_HeadDel(Head);
Test(Head);
// 尾删
CirLkList_TailDel(Head);
Test(Head);
// 指定删
CirLkList_AppointDel(Head, 5);
Test(Head);
}