双向循环链表接口设计

/*************************************************
 *
 *   file name:DoubleDoubleCirLkList.c
 *   author   :momolyl@126.com
 *   date     :2024/04/25
 *   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/25
 *   version       :V1.0
 **************************************************/
// 结构体用于构建链表节点(指针域1+数据域+指针域2)
typedef int Datatype_t;
typedef struct DoubleCirLkList
{
    struct DoubleCirLkList *Prev; // 直接前驱的指针域
    Datatype_t Data;              // 结点的数据域
    struct DoubleCirLkList *Next; // 直接后继的指针域
} DoubleCirLkList_t;

// 创建一个空链表,使用头节点
DoubleCirLkList_t *DoubleCirLkList_Create(void)
{

    DoubleCirLkList_t *Head = (DoubleCirLkList_t *)calloc(1, sizeof(DoubleCirLkList_t)); // 为头节点申请内存,并错误处理
    if (NULL == Head)
    {
        perror("calloc memory for DoubleCirLkList if failed!\n");
        exit(-1);
    }
    Head->Next = Head; // 将头节点的Next指针域指向Head,体现循环链表的特性
    Head->Prev = Head; // 将头节点的Prev指针域指向Head,体现循环链表的特性
    return Head;       // 返回头节点的地址
}

// 创建一个新节点,并对新节点初始化(指针域1+数据域+指针域2)
DoubleCirLkList_t *DoubleCirLkList_NewNode(Datatype_t data)
{

    DoubleCirLkList_t *NewNode = (DoubleCirLkList_t *)calloc(1, sizeof(DoubleCirLkList_t)); // 为新节点申请一个地址,
    if (NULL == NewNode)                                                                    // 申请新节点的错误处理
    {
        perror("calloc memory for NewNode if failed!\n");
        exit(-1);
    }
    NewNode->Data = data;    // 初始化数据域
    NewNode->Prev = NewNode; // 初始化指针域1
    NewNode->Next = NewNode; // 初始化指针域2
    return NewNode;
}

// 向链表中头插一个新节点
void DoubleCirLkList_HeadAdd(DoubleCirLkList_t *Head, Datatype_t data)
{

    DoubleCirLkList_t *NewNode = DoubleCirLkList_NewNode(data);
    if (Head == Head->Next) // 判断链表是否为空
    {
        // 链表为空
        Head->Next = NewNode;    // 让头结点的Next指针指向新结点
        NewNode->Next = NewNode; // 让新结点的Next指针指向新结点
        NewNode->Prev = NewNode; // 让新结点的Prev指针指向新结点
        return;
    }
    // 2.2链表非空
    {
        NewNode->Next = Head->Next;       // 新结点的Next指针指向首结点
        NewNode->Prev = Head->Next->Prev; // 新结点的Prev指针指向尾结点
        Head->Next->Prev->Next = NewNode; // 尾结点的Next指针指向新结点
        Head->Next->Prev = NewNode;       // 首结点的Prev指针指向新结点
        Head->Next = NewNode;             // 头节点的Next指针指向新结点
    }
}

// 向链表中尾插一个新节点
void DoubleCirLkList_TailAdd(DoubleCirLkList_t *Head, Datatype_t data)
{

    DoubleCirLkList_t *Phead = Head->Next;
    DoubleCirLkList_t *NewNode = DoubleCirLkList_NewNode(data);
    if (Head == Phead) // 判断链表是否为空
    {
        // 链表为空
        Head->Next = NewNode;    // 让头结点的Next指针指向新结点
        NewNode->Next = NewNode; // 让新结点的Next指针指向新结点
        NewNode->Prev = NewNode; // 让新结点的Prev指针指向新结点
        return;
    }

    // 2.2链表非空

    NewNode->Prev = Phead->Prev; // 新节点的Prev指针指向尾结点
    NewNode->Next = Phead;       // 新结点的Next指针指向首结点
    Phead->Prev->Next = NewNode; // 尾结点的Next指针指向新结点
    Phead->Prev = NewNode;       // 首结点的Prev指针指向新结点
}

// 向链表中指定数据结点后插入一个新节点
void DoubleCirLkList_AppointAdd(DoubleCirLkList_t *Head, Datatype_t DestVal, Datatype_t data)
{

    DoubleCirLkList_t *NewNode = DoubleCirLkList_NewNode(data);
    if (Head == Head->Next) // 判断链表是否为空
    {
        // 链表为空
        Head->Next = NewNode;    // 让头结点的Next指针指向新结点
        NewNode->Next = NewNode; // 让新结点的Next指针指向新结点
        NewNode->Prev = NewNode; // 让新结点的Prev指针指向新结点
        return;
    }

    // 2.2链表非空
    DoubleCirLkList_t *Phead = Head->Next; // 备份首结点的地址,用于遍历找到目标结点的地址
    DoubleCirLkList_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;
    }

    // 如果成功查找到指定数值,则在指定数据结点后面插入新节点
    if (Phead->Next) // 目标结点为首结点或中间结点
    {
        NewNode->Next = Phead->Next; // 让新结点的Next指针指向目标地址的直接后继结点
        Phead->Next->Prev = NewNode; // 让目标结点的直接后继结点的Prev指针指向新结点
        Phead->Next = NewNode;       // 让目标结点的Next指针指向新结点的地址
        NewNode->Prev = Phead;       // 让新结点的Prev指针指向目标结点的地址
    }
    else // 目标结点为尾结点
    {
        NewNode->Prev = Head->Next->Prev; // 新节点的Prev指针指向尾结点
        NewNode->Next = Head;             // 新结点的Next指针指向首结点
        Head->Prev->Next = NewNode;       // 尾结点的Next指针指向新结点
        Head->Prev = NewNode;             // 首结点的Prev指针指向新结点
    }
}

// 在链表中头删一个节点
bool DoubleCirLkList_HeadDel(DoubleCirLkList_t *Head)
{
    // 判断链表是否为空
    if (Head == Head->Next) // 头节点的Next指针指向头节点的地址时,链表为空
        return false;       // 为空则删除失败

    DoubleCirLkList_t *Phead = Head->Next; // 备份首节点地址,用于最后释放首结点地址
    Phead->Prev->Next = Phead->Next;       // 让尾结点的Next指针指向首节点的直接后继结点
    Phead->Next->Prev = Phead->Prev;       // 让首结点的直接后继结点的Prev指针指向尾结点
    Head->Next = Phead->Next;              // 让头结点的Next指针指向首结点的直接后继结点
    Phead->Next = NULL;                    // 让首结点的Next指针指向NULL
    Phead->Prev = NULL;                    // 让首结点的Prev指针指向NULL
    free(Phead);
    return true;
}

// 在链表中尾删一个节点
bool DoubleCirLkList_TailDel(DoubleCirLkList_t *Head)
{
    // 判断链表是否为空

    // 链表为空
    if (Head == Head->Next) // 头节点的Next指针指向头节点的地址时,链表为空
        return false;       // 为空则删除失败

    // 链表非空
    DoubleCirLkList_t *Phead = Head->Next->Prev; // 备份尾节点地址,用于最后释放首结点地址
    Head->Next->Prev = Phead->Prev;              // 首结点的Prev指针指向尾结点的直接前驱结点
    Phead->Prev->Next = Head->Next;              // 尾结点的直接前驱结点的Next指针指向首结点
    Phead->Prev = NULL;                          // 将尾结点Prev指针指向NULL
    Phead->Next = NULL;                          // 将尾结点的直接前驱结点的Next指针指向NULL
    free(Phead);
    return true;
}

// 在链表中删指定数据结点
bool DoubleCirLkList_AppointDel(DoubleCirLkList_t *Head, Datatype_t DestVal)
{
    // 判断链表是否为空

    // 链表为空
    if (Head == Head->Next) // 头节点的Next指针指向头节点的地址时,链表为空
        return false;       // 为空则删除失败

    // 链表非空

    DoubleCirLkList_t *Phead = Head->Next; // 备份首结点的地址,用于遍历目标节点
    DoubleCirLkList_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 false;
    }
    else // 如果成功查找到指定数值,则删除指定数据结点
    {
        // 1.目标结点为头节点(使用头删函数的代码)
        if (Phead == Head->Next)
        {
            Phead->Prev->Next = Phead->Next; // 让尾结点的Next指针指向
            Phead->Next->Prev = Phead->Prev; // 让首结点的直接后继结点的Prev指针指向尾结点
            Head->Next = Phead->Next;        // 让头结点的Next指针指向首结点的直接后继结点
            Phead->Next = NULL;              // 让首结点的Next指针指向NULL
            Phead->Prev = NULL;              // 让首结点的Prev指针指向NULL
            free(Phead);
        }
        // 2.目标结点为尾节点(使用尾删函数的代码)
        else if (Phead->Next == Head->Next)
        {
            Head->Next->Prev = Phead->Prev; // 首结点的Prev指针指向尾结点的直接前驱结点
            Phead->Prev->Next = Head->Next; // 尾结点的直接前驱结点的Next指针指向首结点
            Phead->Prev = NULL;             // 将尾结点Prev指针指向NULL
            Phead->Next = NULL;             // 将尾结点的直接前驱结点的Next指针指向NULL
            free(Phead);
        }

        // 3.目标结点在中间
        else
        {
            Phead->Prev->Next = Phead->Next; // 让目标结点的直接前驱结点的Next指针指向目标结点的直接后继结点
            Phead->Next->Prev = Phead->Prev; // 让目标结点的直接后继结点的Prev指针指向目标结点的直接前驱结点
            Phead->Next = NULL;
            Phead->Prev = NULL;
            free(Phead);
        }
        return true;
    }
}
// void Test(DoubleCirLkList_t *Head)
// {
//     DoubleCirLkList_t *Phead = Head->Next; // 备份首节点的地址,用于遍历结点
//     while (Phead->Next != Head->Next)      // 遍历到尾结点
//     {
//         printf("%d ", Phead->Data);
//         Phead = Phead->Next;
//     }
//     printf("%d\n", Phead->Data); // 打印尾结点的数据
// }
bool DoubleCirLkList_Print(DoubleCirLkList_t *Head)

{
    DoubleCirLkList_t *Phead = Head->Next; // 备份头结点

    // if (Head == Head->Next) // 判断链表是否为空,为空直接退出

    // {
    //     printf("DoubleCirLkList is empty\n");
    //     return false;
    // }

    while (Phead->Next != Head->Next)
    {
        printf("%d ", Phead->Data); // 打印链表

        Phead = Phead->Next; // 遍历链表

    } // while (Phead != Head); // while (Phead->Next != Head->Next);
    // printf("%p,%p", Phead->Next, Head->Next);
    printf("%d ", Phead->Data);
    printf("\n");
    return true;
}
int main(void)
{
    DoubleCirLkList_t *Head = DoubleCirLkList_Create();
    // 头插
    DoubleCirLkList_HeadAdd(Head, 10);
    DoubleCirLkList_HeadAdd(Head, 20);
    DoubleCirLkList_HeadAdd(Head, 30);
    DoubleCirLkList_HeadAdd(Head, 40);
    DoubleCirLkList_HeadAdd(Head, 50);
    DoubleCirLkList_Print(Head);
    // 尾插
    DoubleCirLkList_TailAdd(Head, 1);
    DoubleCirLkList_TailAdd(Head, 2);
    DoubleCirLkList_TailAdd(Head, 3);
    DoubleCirLkList_TailAdd(Head, 4);
    DoubleCirLkList_TailAdd(Head, 5);
    DoubleCirLkList_Print(Head);
    // 指定插
    DoubleCirLkList_AppointAdd(Head, 5, 88);
    DoubleCirLkList_AppointAdd(Head, 30, 35);
    DoubleCirLkList_Print(Head);
    // return 0;
    // 头删
    DoubleCirLkList_HeadDel(Head);
    DoubleCirLkList_HeadDel(Head);
    DoubleCirLkList_Print(Head);
    // 尾删
    DoubleCirLkList_TailDel(Head);
    DoubleCirLkList_Print(Head);
    // 指定删
    DoubleCirLkList_AppointDel(Head, 1);
    DoubleCirLkList_Print(Head);
}

posted @ 2024-04-27 23:49  铃是铃铛的铃  阅读(3)  评论(0编辑  收藏  举报