双向链表

双向链表(已测试)

/*************************************************
 *
 *   file name:LkList.c
 *   author   :miaowei023@163.com
 *   date     :2025/04/23
 *   brief    :通过构建双向链表学习顺序存储
 *   note     :None
 *
 *   CopyRight (c) 2025    miaowei023@163.com    All Right Reseverd
 *
 **************************************************/
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
/*************************************************
 *
 *   func name     :
 *   brief         :
 *   func parameter:
 *
 *
 *   return        :None
 *   note          :None
 *   func author   :miaowei023@163.com
 *   date          :2025/04/23
 *   version       :V1.0
 **************************************************/
// 结构体用于构建链表节点(指针域1+数据域+指针域2)
typedef int Datatype_t;
typedef struct DoulbeLkList
{
    struct DoulbeLkList *Prev; // 直接前驱的指针域
    Datatype_t Data;           // 结点的数据域
    struct DoulbeLkList *Next; // 直接后继的指针域
} DoulbeLkList_t;

// 创建一个双向空链表,使用头节点
DoulbeLkList_t *DoubleLkList_Create(void)
{

    DoulbeLkList_t *Head = (DoulbeLkList_t *)calloc(1, sizeof(DoulbeLkList_t)); // 为头节点申请内存,并错误处理

    if (NULL == Head)
    {
        perror("calloc memory for DoulbeLkList if failed!\n");
        exit(-1);
    }

    // 对头结点进行初始化,头结点是不存储数据域,指针域指向NULL
    Head->Next = NULL; // 对头结点进行初始化,头结点是不存储数据域,指针域指向NULL
    Head->Prev = NULL;
    return Head; // 返回头节点的地址
}

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

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

    return NewNode;
}
//****************************************************************************21 : 53
// 向链表中头插一个新节点
void DoulbeLkList_HeadAdd(DoulbeLkList_t *Head, Datatype_t data)
{

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

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

    DoulbeLkList_t *NewNode = DoulbeLkList_NewNode(data);
    if (NULL == Head->Next) // 判断链表是否为空
    {
        // 链表为空
        Head->Next = NewNode;
        return;
    }

    // 2.2链表非空
    DoulbeLkList_t *Phead = Head->Next; // 备份首结点的地址
    // 遍历找到尾结点
    while (Phead->Next) // 判断条件为:当前结点的Next指针是否指向NULL
    {
        Phead = Phead->Next; // Phead在循环结束时代表尾结点的地址
    }
    // 在尾端插入新节点
    Phead->Next = NewNode; // 让尾结点的Next指针指向新结点的地址
    NewNode->Prev = Phead; // 让新结点的Prev指针指向尾结点的地址
}

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

    DoulbeLkList_t *NewNode = DoulbeLkList_NewNode(data);
    if (NULL == Head->Next) // 判断链表是否为空
    {
        // 链表为空
        Head->Next = NewNode;
        return;
    }

    // 2.2链表非空
    DoulbeLkList_t *Phead = Head->Next; // 备份首结点的地址
    DoulbeLkList_t *Dest = NULL;        // 用于判断是否成功查找到指定数值的结点
    // 遍历找到指定数据结点
    while (Phead->Data != DestVal) // 判断条件:当前结点的数据域的数据是否与目标值不同
    {
        Phead = Phead->Next;                               // 不同则偏移
        if (Phead->Next == NULL && 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 = Phead; // 让新结点的Prev指针指向目标结点
        Phead->Next = NewNode; // 让目标结点的Next指针指向新结点
    }
}

// 在链表中头删一个节点
bool DoulbeLkList_HeadDel(DoulbeLkList_t *Head)
{
    // 判断链表是否为空
    if (NULL == Head->Next) // 判断链表是否为空
    {
        // 链表为空
        return false;
    }

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

// 在链表中尾删一个节点
bool DoulbeLkList_TailDel(DoulbeLkList_t *Head)
{
    // 判断链表是否为空
    if (NULL == Head->Next) // 判断链表是否为空
    {
        // 链表为空
        return false;
    }

    DoulbeLkList_t *Phead1 = Head->Next; // 备份首节点地址,用于遍历查找尾结点
    DoulbeLkList_t *Phead2 = Head;       // 备份头节点地址,用于遍历查找尾结点的直接前驱结点

    // 遍历查找尾结点
    while (Phead1->Next) // 判断条件为:当前结点的Next指针是否指向首结点
    {
        Phead1 = Phead1->Next; // Phead1在循环结束时代表尾结点的地址
        Phead2 = Phead2->Next; // Phead2在循环结束时代表尾结点的直接前驱结点的地址
    }
    Phead1->Prev = NULL; // 将尾结点Prev指针指向NULL
    Phead2->Next = NULL; // 将尾结点的直接前驱结点的Next指针指向NULL
    free(Phead1);
    return true;
}

// 在链表中删指定数据结点
bool DoulbeLkList_AppointDel(DoulbeLkList_t *Head, Datatype_t DestVal)
{
    // 判断链表是否为空
    if (NULL == Head->Next) // 判断链表是否为空
    {
        // 链表为空
        return false;
    }

    DoulbeLkList_t *Phead1 = Head->Next; // 备份首结点的地址,用于遍历目标节点
    DoulbeLkList_t *Phead2 = Head;       // 备份头结点的地址,用于遍历目标节点的前驱结点
    DoulbeLkList_t *Dest = NULL;         // 用于判断是否成功查找到指定数值的结点
    // 遍历找到指定数据结点
    while (Phead1->Data != DestVal) // 判断条件:当前结点的数据域的数据是否与目标值不同
    {
        Phead1 = Phead1->Next;                               // 不同则指向目标结点的指针偏移
        Phead2 = Phead2->Next;                               // 不同则指向目标结点的直接前驱的指针偏移
        if (Phead1->Next == NULL && 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)
        {

            DoulbeLkList_t *Phead = Head->Next; // 备份首节点地址,用于最后释放首结点地址
            Head->Next = Phead->Next;           // 让头结点的Next指针指向首结点的直接后继结点
            Phead->Next = NULL;                 // 让首结点的Next指针指向NULL
            Head->Next->Prev = NULL;            // 让首结点的直接后继结点的Prev指针指向NULL
            free(Phead);
        }
        // 2.目标结点为尾节点(使用尾删函数的代码)
        else if (Phead1->Next == NULL)
        {
            DoulbeLkList_t *Phead3 = Head->Next; // 备份首节点地址,用于遍历查找尾结点
            DoulbeLkList_t *Phead4 = Head;       // 备份头节点地址,用于遍历查找尾结点的直接前驱结点

            // 遍历查找尾结点
            while (Phead1->Next) // 判断条件为:当前结点的Next指针是否指向首结点
            {
                Phead3 = Phead3->Next; // Phead1在循环结束时代表尾结点的地址
                Phead4 = Phead4->Next; // Phead2在循环结束时代表尾结点的直接前驱结点的地址
            }
            Phead3->Prev = NULL; // 将尾结点Prev指针指向NULL
            Phead4->Next = NULL; // 将尾结点的直接前驱结点的Next指针指向NULL
            free(Phead3);
        }

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

{
    DoulbeLkList_t *Phead = Head; // 备份头结点

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

    {

        printf("doubLinkedList is empty\n");
        return false;
    }

    while (Phead->Next)

    {

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

        printf(" %d", Phead->Data); // 打印链表
    }

    printf("\n");

    return true;
}

int main(void)
{
    DoulbeLkList_t *Head = DoubleLkList_Create();
    // 测试头插
    DoulbeLkList_HeadAdd(Head, 10);
    DoulbeLkList_HeadAdd(Head, 20);
    DoulbeLkList_HeadAdd(Head, 30);
    DoulbeLkList_HeadAdd(Head, 40);
    DoulbeLkList_HeadAdd(Head, 50);
    doubleLinkedList_Print(Head);
    // Test(Head);

    // 测试尾插
    DoulbeLkList_TailAdd(Head, 1);
    DoulbeLkList_TailAdd(Head, 2);
    DoulbeLkList_TailAdd(Head, 3);
    DoulbeLkList_TailAdd(Head, 4);
    DoulbeLkList_TailAdd(Head, 5);
    doubleLinkedList_Print(Head);
    // Test(Head);

    // 测试指定插
    DoulbeLkList_AppointAdd(Head, 20, 77);
    DoulbeLkList_AppointAdd(Head, 5, 88);
    DoulbeLkList_AppointAdd(Head, 50, 66);
    doubleLinkedList_Print(Head);

    // 测试头删
    DoulbeLkList_HeadDel(Head);
    DoulbeLkList_HeadDel(Head);
    DoulbeLkList_HeadDel(Head);
    DoulbeLkList_HeadDel(Head);
    // Test(Head);

    // 测试尾删
    DoulbeLkList_TailDel(Head);
    DoulbeLkList_TailDel(Head);
    DoulbeLkList_TailDel(Head);
    doubleLinkedList_Print(Head);
    // Test(Head);

    // 测试指定插
    DoulbeLkList_AppointDel(Head, 77);

    doubleLinkedList_Print(Head);
    return 0;
}


posted @ 2025-07-17 11:48  喵喵机023  阅读(10)  评论(0)    收藏  举报