单向循环链表

单向循环链表(测试通过)

/*************************************************
 *
 *   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);
}

posted @   铃是铃铛的铃  阅读(21)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示